File Sharing Site in Python - 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/

Related

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.

Write to a file on a server using python

I want to write some data, e.g. "hello" on a file that is located on a remote server, not a local server. This is the code that I used to read from server:
import urllib2
var = "hello"
url = "http://url:port/log/log.txt"
response = urllib2.urlopen(url)
txt = response.read();
print txt
As an output I was able to get the data from the log file.
Now I want to write some data, e.g. "hello" onto the same file. How to achieve that?
What your code is doing is actually not "reading a file" but sending an HTTP get request to a given url and print the HTTP response's body. What you get as content for this url is up to the HTTP server serving this url, and the fact it actually comes from a file stored on this server is totally irrelevant - it might as well come from a database, from another web server, or be typed in real time by a monkey FWIW.
If you want to use the HTTP protocol to modify the content of a given file on this server, the server must provide this service (as a given url where you're supposed to send a POST or PUT http request with the new content).

BaseHTTPServer only Serves Webpage Once

I'm trying to figure out how to set up a basic web server in Python, but I'm having a lot of difficulty.
My main issue is that I am only able to get my server to serve the webpage once. The html displays a message in the browser and the Javascript displays another message in the console.
When I start the server and go to http://127.0.0.1:8080, both of my messages display and everything is fine. However, I run into problems when I open up a second browser tab and go there again. I get the GET HTTP request in the terminal, but not the GET Javascript request. And nothing displays in either the browser window or the console.
What am I doing wrong? Any suggestions would be appreciated.
Here is my Python code:
import BaseHTTPServer
from os import curdir, sep
htmlfile="htmltest.html"
htmlpage =open(curdir+sep+htmlfile, 'rb')
jsfile="jstest.js"
jspage=open(curdir+sep+jsfile, 'rb')
notfound = "File not found"
class WelcomeHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_OPTIONS(self):
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With")
def do_GET(self):
if self.path == "/":
print "get html"
self.send_response(200)
self.send_header("Content-type","text/html")
self.end_headers()
self.wfile.write(htmlpage.read())
elif self.path=="/jstest.js":
print "get js"
self.send_response(200)
self.send_header("Content-type","text/js")
self.end_headers()
self.wfile.write(jspage.read())
else:
self.send_error(404, notfound)
httpserver = BaseHTTPServer.HTTPServer(("127.0.0.1",8080), WelcomeHandler)
#httpserver.serve_forever()
while True:
httpserver.handle_request()
When you open a file in Python and read its contents, the "file pointer" (i.e. where the next read will start from) is then at the end of the file. You'll either have to re-open it or rewind to the beginning of the file in order to read it again.
Unless you expect your files to change frequently, you might want to just read them at the start and store the contents in a variable, then serve that. Alternatively, you could move your opening into your do_GET method so it opens it fresh for each request.

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

Django: how to give mp3 file correctly

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

Categories

Resources