Django: how to give mp3 file correctly - python

The problem is I can't change playing position by clicking on a timeline in google Chrome (it always plays from start to end)
If Nginx gives mp3 file to the client everything is OK and I can change playing position.
In my script I give mp3 this way:
from django.core.servers.basehttp import FileWrapper
wrapper = FileWrapper(file( 'mp3file.mp3' ))
response = HttpResponse(wrapper, content_type='audio/mpeg')
response['Content-Length'] = os.path.getsize( 'mp3file.mp3' )
return response
The url is: http://server/mp3/###.mp3
So the whole file is given to the client, but still playing pos can't be changed. What is wrong?
PS:
Do not use any proprietary sh*t like mp3 - use ".ogg" format

This is because the headers should handle additiona headers (like Accept-Ranges), and it should handle partial file requests
Doing this kind of things inside Django itself is a mess (I tried it some time ago), but then I ended up using Apache for serving files (this way you just don't waste resources)
You can consider using mod_xsendfile for being able to serve files from your django view using apache, in this way for example:
response = HttpResponse(mimetype='audio/mpeg')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['Accept-Ranges'] = 'bytes'
response['X-Sendfile'] = smart_str(path_to_file)
return response

Related

Browser not playing audio returned with Python Flask

I have a simple Python Flask application I'm using to return images and audio I generate server side (separately). My image code looks like this:
# various cv2 calls to generate an image
image_bytes = cv2.imencode('.png', image)
return Response(image_bytes[1].tobytes(), mimetype='image/png', status=200)
That works fine. However, I can't seem to get audio returned. Here's that code:
AUDIO_FORMAT = 'audio/mp3'
headers = {"Content-Type": "application/json", "accept": AUDIO_FORMAT}
payload = {"text": text}
# call to generate audio from text
response = requests.post()
if response.status_code == 200:
sound_data = response.content
f = open('audio.mp3', 'wb')
f.write(sound_data)
f.close()
return Response(sound_data,
mimetype=AUDIO_FORMAT,
status=200)
When I play the audio.mp3 file, I hear the audio I expect. And when the response renders in the browser, I get the audio player. But I hear nothing and I get a length of 00:00. I'm thinking there's an encoding/translation step I'm missing.
As it turns out, I was on the right path writing the audio to a file and returning that file. In my first comment, I stated that writing the audio to a file and using send_from_directory was failing. Well, it was, but because of a separate error. Once I fixed that, it worked.
return send_from_directory('static/audio', file_name)
I think I understand why my original approach was not working. When the browser saw the audio mimetype, it attempted to play it with the audio control. That audio control has a src attribute pointing back to the audio I sent. The subsequent call back to my server from the audio control correctly fails, because I didn't persist that audio and provide a path to it.

How can I display/save an xlsx file that I received from a server with http requests in python?

I am building an application where data is sent to a server, the server creates an xlsx (excel) file with that data and returns that file to the client where at the end I want it to be displayed
Im using flask and the creation of the file itself with the data from the client works and the file is saved locally in the same folder. I tried several things but I cant seem to check wether the file was sent back correctly because I dont exactly know how to work with it on the client side. Currently I try sending the file back as following:
return send_file("my_file.xlsx", as_attachment=True)
and I also tried
return send_file("absolute/path/to/my_file", as_attachment=True)
On the client side I also tried all kind of things and Im currently at
print(r.content)
which prints tons of characters, backslashes etc..
and where r is
r = requests.get('http://127.0.0.1:5000/', params = {...})
So two problems:
I dont know if the file is correctly sent from the server, how can I check?
Probably answers the first one: How can I display or save the file on the client side?
The file is created with xlsxwriter and I dont get any error messages. Return status also is 200 so I guess my problem is opening the file on the client side. But if anybody has advice I would be really happy to hear!
Edit: File was sent correctly, the answer was:
r = requests.get('http://127.0.0.1:5000/', params = {...})
def save_xl(r):
with open('file.xlsx', 'wb') as f:
f.write(r.content)
save_xl(r)
And the file was create successfully
you can try saving the content of the request as a xlsx file.
r = requests.get('http://127.0.0.1:5000/', params = {...})
def save_xl(r):
with open('file.xlsx', 'wb') as f:
f.write(r.content)
save_xl(r)

Flask redirect to external file and provide filename instead of original

I use Flask to serve application. Until recently, Flask was serving files for download using response stream.
Because of increased number of worker errors, I now need to redirect request to external location, where files are stores in 7249ed01-9c3d-45fe-895c-5a27db785d2d.tar.gz filename format. Redirect itself works, but filename displayed in save dialog in browser is still 7249ed01-9c3d-45fe-895c-5a27db785d2d.
Code example is below.
#app.route("/download/<filename>")
def view_file_download(filename):
# filename can be ignored, used to retrieve info from db
url = "https://example.com/7249ed01-9c3d-45fe-895c-5a27db785d2d.tar.gz"
filename_orig = "example.tar.gz"
filesize = 123456
res = flask.redirect(url, code=303)
res.headers.set("Location", url)
res.headers.set("Content-Disposition", "attachment", filename=filename_orig)
res.headers.set("Content-Length", filesize)
return res
It is not possible to provide content-disposition header for redirect response.
Major browsers do not support this feature any longer. It is required that server serving files contain content-disposition in own response and it will tell browser to use that instead of hashed filename.

Send mail per request in django

This is really killing me. I've been dealing with this for days.
When a user download a file from my django web app, I want to notify the uploader that his file has been downloaded by sending a mail. The problem is, If I should download a low file size (489kb), it will send a mail once to the uploader. But if I should download a file size of 3mb or above it will send more than one mail to the uploader.
I just want it to send one mail notification to the uploader per download.
views:
#login_required
def document_view(request,emov_id):
fileload = Emov.objects.get(id=emov_id)
filename = fileload.mov_file.name.split('/')[-1]
filesize=fileload.mov_file.size
response = HttpResponse(fileload.mov_file, content_type='')
response['Content-Disposition'] = 'attachment; filename=%s' % filename
response['Content-Length'] = filesize
send_mail('Your file has just been downloaded',loader.get_template('download.txt').render(Context({'fileload':fileload})),'test#example.com',[fileload.email,])
return response
download.txt
'Your file {{ fileload.name}} have been downloaded!'
How can I send mail per download request?
I would suggest a different approach...
When someone download the file, log the event to a table on your database.
Write the Session ID, the file name, the user name.
Make sure that session_id+file_name+user_name are unique key
This way, you can get much more information that can help you later.
Later on (as a crontab batch, or save listener) send the emails.
You can even send a daily/weekly report and so on...
I think you would solve this problem just with following best practises which say "Do not serve files with Django".
Instead, use X-Sendfile HTTP header in your response and configure your webserver to catch it and serve the file. See this if you're using Apache.
Then, create the response as follows:
response = HttpResponse()
response['X-Sendfile'] = unicode(filename).encode('utf-8')
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response['Content-length'] = filesize # Optional
return response

File Sharing Site in Python

I wanted to design a simple site where one person can upload a file, and pass off the random webaddress to someone, who can then download it.
At this point, I have a webpage where someone can successfully upload a file which gets stored under /files/ on my webserver.
The python script also generates a unique, random 5 letter code that gets stored in a database identifying the file
I have another page called retrieve, where a person should go, put in the 5 letter code, and it should pop up a filebox asking where to save the file.
My Problem is that: 1) How do I retrieve the file for download? At this point my retrieve script, takes the code, gets the location of the file on my server, but how do I get the brower to start downloading?
2)How do I stop people from directly going to the file? Should I change permissions on the file?
How do you serve the file-upload page, and how do you let your users upload files?
If you are using Python's built-in HTTP server modules you shouldn't have any problems.
Anyway, here's how the file serving part is done using Python's built-in modules (just the basic idea).
Regarding your second question, if you were using these modules in the first place you probably wouldn't have asked it because you'd have to explicitly serve specific files.
import SocketServer
import BaseHTTPServer
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
# The URL the client requested
print self.path
# analyze self.path, map the local file location...
# open the file, load the data
with open('test.py') as f: data = f.read()
# send the headers
self.send_response(200)
self.send_header('Content-type', 'application/octet-stream') # you may change the content type
self.end_headers()
# If the file is not found, send error code 404 instead of 200 and display a message accordingly, as you wish.
# wfile is a file-like object. writing data to it will send it to the client
self.wfile.write(data)
# XXX: Obviously, you might want to send the file in segments instead of loading it as a whole
if __name__ == '__main__':
PORT = 8080 # XXX
try:
server = SocketServer.ThreadingTCPServer(('', 8080), RequestHandler)
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()
You should send the right HTTP Response, containing the binary data and making the browser react on it.
Try this (I haven't) if you're using Django:
response = HttpResponse()
response['X-Sendfile'] = os.path.join(settings.MEDIA_ROOT, file.file.path)
content_type, encoding = mimetypes.guess_type(file.file.read())
if not content_type:
content_type = 'application/octet-stream'
response['Content-Type'] = content_type
response['Content-Length'] = file.file.size
response['Content-Disposition'] = 'attachment; filename="%s"' % file.file.name
return response
Source: http://www.chicagodjango.com/blog/permission-based-file-serving/

Categories

Resources