django: how to correctly specify output-download file-type (in this case mp3)? - django

I have a simple django platform where I can upload text files. Ultimately I want to return a downloadable mp3 audio file made from the text in the uploaded file. My problem currently is that I cannot seem to correctly specify the type of file that the website outputs for download.
I then tried to make the downloadable output of the website an mp3 file:
views.py (code adapted from https://github.com/sibtc/simple-file-upload)
def simple_upload(request):
if request.method == 'POST' and request.FILES['myfile']:
myfile = request.FILES['myfile']
print(str(request.FILES['myfile']))
x=str(myfile.read())
tts = gTTS(text=x, lang='en')
response=HttpResponse(tts.save("result.mp3"),content_type='mp3')
response['Content-Disposition'] = 'attachment;filename=result.mp3'
return response
return render(request, 'core/simple_upload.html')
Upon pressing the upload button, the text-to-speech conversion is successful but the content_type of the response is not definable as 'mp3'. The file that results from the download is result.mp3.txt and it contains 'None'.

Can you try to prepare your response using the sample code below?
I've managed to return CSV files correctly this way so it might help you too.
Here it is:
HttpResponse(content_type='text/plain') # Plain text file type
response['Content-Disposition'] = 'attachment; filename="attachment.txt"' # Plain text file extension
response.write("Hello, this is the file contents.")
return response

There are two problems I can see here. The first is that tts.save() returns None, and that is getting passed directly to the HttpResponse. Secondly, the content_type is set to mp3 and ought to be set to audio/mp3.
After calling tts.save(), open the mp3 and pass the file handle to the HttpResponse, and then also set the content_type correctly - for example:
def simple_upload(request):
if request.method == 'POST' and request.FILES['myfile']:
...
tts.save("result.mp3")
response=HttpResponse(open("result.mp3", "rb"), content_type='audio/mp3')

Related

How to zip multiple uploaded file in Django before saving it to database?

I am trying to compress a folder before saving it to database/file storage system using Django. For this task I am using ZipFile library. Here is the code of view.py:
class BasicUploadView(View):
def get(self, request):
file_list = file_information.objects.all()
return render(self.request, 'fileupload_app/basic_upload/index.html',{'files':file_list})
def post(self, request):
zipfile = ZipFile('test.zip','w')
if request.method == "POST":
for upload_file in request.FILES.getlist('file'): ## index.html name
zipfile.write(io.BytesIO(upload_file))
fs = FileSystemStorage()
content = fs.save(upload_file.name,upload_file)
data = {'name':fs.get_available_name(content), 'url':fs.url(content)}
zipfile.close()
return JsonResponse(data)
But I am getting the following error:
TypeError: a bytes-like object is required, not 'InMemoryUploadedFile'
Is there any solution for this problem? Since I may have to upload folder with large files, do I have to write a custom TemporaryFileUploadHandler for this purpose? I have recently started working with Django and it is quite new to me. Please help me with some advice.
InMemoryUploadedFile is an object that contains more than just file you should open file and read it content ( InMemoryUploadedFile.file is the file)
InMemoryUploadedFile.open()
You should open file with open() and then read() it's content, also you should check if you have uploaded files correctly also you could use with syntax for both zip and file
https://www.pythonforbeginners.com/files/with-statement-in-python

How to access file after upload in Django?

I'm working on a web. User can upload a file. This file is in docx format. After he uploads a file and choose which languages he wants to translate the file to, I want to redirect him to another page, where he can see prices for translations. The prices depends on particular language and number of characters in the docx file.
I can't figure out how to handle the file uploaded. I have a function which get's path to file and returns a number of characters. After uploading file and click on submit, I want to call this function so I can render new page with estimated prices.
I've read that I can call temporary_file_path on request.FILES['file'] but it raises
'InMemoryUploadedFile' object has no attribute 'temporary_file_path'
I want to find out how many characters uploaded file contains and send it in a request to another view - /order-estimation.
VIEW:
def create_order(request):
LanguageLevelFormSet = formset_factory(LanguageLevelForm, extra=5, max_num=5)
language_level_formset = LanguageLevelFormSet(request.POST or None)
job_creation_form = JobCreationForm(request.POST or None, request.FILES or None)
context = {'job_creation_form': job_creation_form,
'formset': language_level_formset}
if request.method == 'POST':
if job_creation_form.is_valid() and language_level_formset.is_valid():
cleaned_data_job_creation_form = job_creation_form.cleaned_data
cleaned_data_language_level_formset = language_level_formset.cleaned_data
for language_level_form in [d for d in cleaned_data_language_level_formset if d]:
language = language_level_form['language']
level = language_level_form['level']
Job.objects.create(
customer=request.user,
text_to_translate=cleaned_data_job_creation_form['text_to_translate'],
file=cleaned_data_job_creation_form['file'],
short_description=cleaned_data_job_creation_form['short_description'],
notes=cleaned_data_job_creation_form['notes'],
language_from=cleaned_data_job_creation_form['language_from'],
language_to=language,
level=level,
)
path = request.FILES['file'].temporary_file_path
utilities.docx_get_characters_number(path) # THIS NOT WORKS
return HttpResponseRedirect('/order-estimation')
else:
return render(request, 'auth/jobs/create-job.html', context=context)
return render(request, 'auth/jobs/create-job.html', context=context)
The InMemoryUploadedFile does not provide temporary_file_path. The content lives 'in memory' - as the class name implies.
By default Django uses InMemoryUploadedFile for files up to 2.5MB size, larger files use TemporaryFileUploadHandler. where the later provides the temporary_file_path method in question. Django Documentation
So an easy way would be to change your settings for FILE_UPLOAD_HANDLERS to always use TemporaryFileUploadHandler:
FILE_UPLOAD_HANDLERS = [
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]
Just keep in mind that this is not the most efficient way when you have a site with a lot of concurrent small upload requests.

django RequestFactory file upload

I try to create a request, using RequestFactory and post with file, but I don't get request.FILES.
from django.test.client import RequestFactory
from django.core.files import temp as tempfile
tdir = tempfile.gettempdir()
file = tempfile.NamedTemporaryFile(suffix=".file", dir=tdir)
file.write(b'a' * (2 ** 24))
file.seek(0)
post_data = {'file': file}
request = self.factory.post('/', post_data)
print request.FILES # get an empty request.FILES : <MultiValueDict: {}>
How can I get request.FILES with my file ?
If you open the file first and then assign request.FILES to the open file object you can access your file.
request = self.factory.post('/')
with open(file, 'r') as f:
request.FILES['file'] = f
request.FILES['file'].read()
Now you can access request.FILES like you normally would. Remember that when you leave the open block request.FILES will be a closed file object.
I made a few tweaks to #Einstein 's answer to get it to work for a test that saves the uploaded file in S3:
request = request_factory.post('/')
with open('my_absolute_file_path', 'rb') as f:
request.FILES['my_file_upload_form_field'] = f
request.FILES['my_file_upload_form_field'].read()
f.seek(0)
...
Without opening the file as 'rb' I was getting some unusual encoding errors with the file data
Without f.seek(0) the file that I uploaded to S3 was zero bytes
You need to provide proper content type, proper file object before updating your FILES.
from django.core.files.uploadedfile import File
# Let django know we are uploading files by stating content type
content_type = "multipart/form-data; boundary=------------------------1493314174182091246926147632"
request = self.factory.post('/', content_type=content_type)
# Create file object that contain both `size` and `name` attributes
my_file = File(open("/path/to/file", "rb"))
# Update FILES dictionary to include our new file
request.FILES.update({"field_name": my_file})
the boundary=------------------------1493314174182091246926147632 is part of the multipart form type. I copied it from a POST request done by my webbrowser.
All the previous answers didn't work for me. This seems to be an alternative solution:
from django.core.files.uploadedfile import SimpleUploadedFile
with open(file, "rb") as f:
file_upload = SimpleUploadedFile("file", f.read(), content_type="text/html")
data = {
"file" : file_upload
}
request = request_factory.post("/api/whatever", data=data, format='multipart')
Be sure that 'file' is really the name of your file input field in your form.
I got that error when it was not (use name, not id_name)

Django (audio) File Validation

I'm experimenting with a site that will allow users to upload audio files. I've read every doc that I can get my hands on but can't find much about validating files.
Total newb here (never done any file validation of any kind before) and trying to figure this out. Can someone hold my hand and tell me what I need to know?
As always, thank you in advance.
You want to validate the file before it gets written to disk. When you upload a file, the form gets validated then the uploaded file gets passed to a handler/method that deals with the actual writing to the disk on your server. So in between these two operations, you want to perform some custom validation to make sure it's a valid audio file
You could:
check if the the file is less then a certain size (good practice)
then check if the submitted file has a certain content type (i.e. an audio file)
this is pretty useless as someone could easily spoof it
then check that the file ends in a certain extension (or extensions)
this is also pretty useless
try read the file and see if it's actually audio
(I haven't tested this code)
models.py
class UserSong(models.Model):
title = models.CharField(max_length=100)
audio_file = models.FileField()
forms.py
class UserSongForm(forms.ModelForm):
# Add some custom validation to our file field
def clean_audio_file(self):
file = self.cleaned_data.get('audio_file',False):
if file:
if file._size > 4*1024*1024:
raise ValidationError("Audio file too large ( > 4mb )")
if not file.content-type in ["audio/mpeg","audio/..."]:
raise ValidationError("Content-Type is not mpeg")
if not os.path.splitext(file.name)[1] in [".mp3",".wav" ...]:
raise ValidationError("Doesn't have proper extension")
# Here we need to now to read the file and see if it's actually
# a valid audio file. I don't know what the best library is to
# to do this
if not some_lib.is_audio(file.content):
raise ValidationError("Not a valid audio file")
return file
else:
raise ValidationError("Couldn't read uploaded file")
views.py
from utils import handle_uploaded_file
def upload_file(request):
if request.method == 'POST':
form = UserSongForm(request.POST, request.FILES)
if form.is_valid():
# If we are here, the above file validation has completed
# so we can now write the file to disk
handle_uploaded_file(request.FILES['file'])
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
return render_to_response('upload.html', {'form': form})
utils.py
# from django's docs
def handle_uploaded_file(f):
ext = os.path.splitext(f.name)[1]
destination = open('some/file/name%s'%(ext), 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#file-uploads
https://docs.djangoproject.com/en/dev/ref/forms/fields/#filefield
https://docs.djangoproject.com/en/dev/ref/files/file/#django.core.files.File

Django download file empty

I am writing a simple function for downloading a certain file, from the server, to my machine.
The file is unique represented by its id. The file is locatd corectly, and the download is done, but the downloaded file (though named as the one on the server) is empty.
my download function looks like this:
def download_course(request, id):
course = Courses.objects.get(pk = id).course
path_to_file = 'root/cFolder'
filename = __file__ # Select your file here.
wrapper = FileWrapper(file(filename))
content_type = mimetypes.guess_type(filename)[0]
response = HttpResponse(wrapper, content_type = content_type)
response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(course)
return response
where can i be wrong? thanks!
I answered this question here, hope it helps.
Looks like you're not sending any data (you don't even open the file).
Django has a nice wrapper for sending files (code taken from djangosnippets.org):
def send_file(request):
"""
Send a file through Django without loading the whole file into
memory at once. The FileWrapper will turn the file object into an
iterator for chunks of 8KB.
"""
filename = __file__ # Select your file here.
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Length'] = os.path.getsize(filename)
return response
so you could use something like response = HttpResponse(FileWrapper(file(path_to_file)), mimetype='application/force-download').
If you are really using lighttpd (because of the "X-Sendfile" header), you should check the server and FastCGI configuration, I guess.
Try one of these approaches:
1) Disable GZipMiddleware if you are using it;
2) Apply a patch to django/core/servers/basehttp.py described in
https://code.djangoproject.com/ticket/6027