How to allow dynamically created file to be downloaded in CherryPy? - python

I'm trying to use CherryPy for a simple website, having never done Python web programming before.
I'm stuck trying to allow the download of a file that is dynamically created. I can create a file and return it from the handler, or call serve_fileobj() on the file, but in either case the contents of the file are simply rendered to the screen, rather than downloaded.
Does CherryPy offer any useful methods here? How can this be accomplished?

If you set the correct content type, you won't have to worry about it rendering in the browser when you return it unless it's appropriate. Try:
response.headers['Content-Type'] = 'application/foo'
(or whatever the correct MIME type for your content is) before you return the content.

Add 'Content-Disposition: attachment; filename="<file>"' header to response

Putting the previous answers from #varela and #JasonFruit together:
dynamic_content = "this was generated on the fly!"
cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="<file>"'
return dynamic_content

Related

Using URL of file as file path in Python in Lambda

I am trying to acquire a file from a url on the web and then open that file for use in an application I’m making in python on AWS Lambda. There doesn’t seem to be a way for me to acquire the file in the form I need it, which I believe to be an os.Pathlike object.
Here is what I am trying now, which doesn’t work since requests.get returns a response not path. I’m posting from a phone right now so I cannot use code tags. Apologies.
filename = requests.get(“url.com/file.txt”)
f = open(filename, ‘rb’)
I have also tried a urlparse and a urllib urlretrieve on the url but that does not return a pathlike object either. Note that I don’t believe I can just use wget or something on the shell level as I am using AWS lambda.
import requests
url = 'http://url.com/file.txt'
r = requests.get(url, allow_redirects=True)
f = open(r, ‘rb’)
When you do such operation, it's always a good to see the entire response of the request you are doing. I usually use the dict attribute, works quite often
print(response.__dict__)
On the ones I have done, there were a _content field in the response object with the file bytes. Then you can simply use the io module to read this file :
file = io.BytesIO(response._content)
This can then be used as a file just like when you do open() function

Open URL or local file in Python 3

I have a string.
I want:
If this string is an URL, I want to open this URL.
Otherwise, I want to open a local file with this name.
If there is no such object, raise an exception.
What is the correct and easy enough (if possible) way to do it in Python 3?
The main issue is the correct way to determine if a string is an URL.
Depends on what you mean by URL. If it's a web address, it will most usually start with http:// or https:// (usually, those are the cases you care about, anyway). However, it is possible that is also starts with ftp:// or some other protocol. However, most libraries accept URIs, which includes file URI. In that scheme, a file location looks like a URL that starts with file://, so you could pass your string, not caring whether it's a web address or a file, and the library will take care of it. There is no straight way of knowing whether the address is valid, but the library will throw an exception if it's not.
Try this:
import os
import webbrowser
import requests
webbrowser.open(s) if os.path.isfile(s) or requests.get(s) else exec("raise Exception")
This hasn't been tested, so treat it as psuedocode.
if s.startswith('http'):
# Do something
elif os.path.isfile(s):
file = open(s, 'r')
else:
raise Exception

Parse and process CSV in Django

Apologies in advance since I'm new to Django (and I've also had to freshen up my Python skills). I'm trying to make a simple example of uploading a file through a form and then printing the rows in my terminal (as a test before doing some actual processing). My views.py contains the following:
def upload_csv(request):
if "GET" == request.method:
return render(request, "portal/csvupload.html")
csv_file = request.FILES["csv_file"]
handle_files(csv_file)
return HttpResponseRedirect(reverse("portal:csvupload"))
def handle_files(csvfile):
csv_reader = csv.reader(csvfile)
for line in csv_reader:
print(line)
Now this returns an error message saying "expected str, bytes or os.PathLike object, not InMemoryUploadedFile", and I'm unsure what's wrong with the code based on the error message? From a Python perspective it looks fine I think, but perhaps it's something to do with the re-direct? Apperciate all answers
request.FILES["csv_file"] is returning an InMemoryUploadedFile object and csv.reader does not know how to handle such an object. I believe you need to call the object's read method: handle_files(csv_file.read()). Note the warning in the documentation: "Be careful with this method: if the uploaded file is huge it can overwhelm your system if you try to read it into memory. You’ll probably want to use chunks() instead; see below."

In Python, urllib.urlretrieve downloads a file which says "Go away"

I'm trying to download the (APK) files from links such as https://www.apkmirror.com/wp-content/themes/APKMirror/download.php?id=215041. When you enter the link in your browser, it brings up a dialog to open or save the file (see below).
I would like to save the file using a Python script. I've tried the following:
import urllib
download_link = 'https://www.apkmirror.com/wp-content/themes/APKMirror/download.php?id=215041'
download_file = '/tmp/apkmirror_test/youtube.apk'
if __name__ == "__main__":
urllib.urlretrieve(url=download_link, filename=download_file)
but the resulting youtube.apk contains only the words "Go away".
Since I am able to download the file by pasting the link in my browser's address bar, there must be some difference between that and urllib.urlretrieve that makes this not work. Can someone explain this difference and how to eliminate it?
You should not programmatically access that download page as it is disallowed in the robots.txt:
https://www.apkmirror.com/robots.txt
That being said, your request header is different. Python by default sets User-Agent to something like "Python...". That is the most likely cause of detection.

hgweb raw view returns the wrong content-type

Background: I'm using https://bitbucket.org/mariocesar/django-hgwebproxy/wiki/Home to add a Mercurial browser to a Django site I'm building.
The problem I'm having is: The particular files we're storing in the HG repo are bind zone files and happen to be named /some/path/somedomain.com which is causing hgweb to set the content-type to application/x-msdos-program (when the content is really text/plain) when returning the raw view of the file. The incorrect content-type is causing hgwebproxy to dump the content into the page template, rather than just return it. It does a test like this to skip templating:
if response['content-type'].split(';')[0] in ('application/octet-stream', 'text/plain'):
return response
Some posible solutions are of course
Rename all the files to .zone (Lame and time consuming)
Hack hgwebproxy to pass application/x-msdos-program (Lame and dirty)
Convince hgweb to use the correct content-type (Awesome! I hope you'll help)
hgweb uses mimetypes to detect the mime type of a file. You might be able to override the ".com" suffix detection by adding a settings file. See: mimetypes.knownfiles:
>>> import mimetypes
>>> mimetypes.init()
>>> mimetypes.knownfiles
['/etc/mime.types', '/etc/httpd/mime.types', '/etc/httpd/conf/mime.types', '/etc/apache/mime.types', '/etc/apache2/mime.types', '/usr/local/etc/httpd/conf/mime.types', '/usr/local/lib/netscape/mime.types', '/usr/local/etc/httpd/conf/mime.types', '/usr/local/etc/mime.types']

Categories