Server side problems saving a file uploaded using requests.POST - django

Below is my python snippet code to upload a file on certain server:
import requests
url = "http://<my_url>"
files = {'file':open("<file to be uploaded>", "rb"), "name":"<name of the file>"}
r = requests.post(url, files=files)
Status code is 200, so it's OK.
My question is, on the server side, how do I save this file?
I can access 'name' on the dict thru this:
request.POST.get('name')
I can access the 'file' as well but I can't save it. I am trying this:
ufile = request.POST.get('file')
dest = open("<file on the server side>", "wb+")
for chunk in ufile.chunks():
dest.write(chunk)
dest.close()
But this is not working and it throws this exception:
unicode object has no attribute 'chunks'
I also tried:
ufile.save(<"filepath on the server side">)
But I encountered the same exception.
Hoping for any feedback! Thanks in advance!

Files are not in request.POST, they are in request.FILES.
But you probably want to read the file uploads documentation.

Try the following instead of writing chunk by chunk:
dest.write(ufile)
The ufile object is a string, not a file handle.

Had the same issue..
You should delete "Content-type" from request header, and your file will appear in request.FILES, then you can work with <TemporaryUploadedFile>
I think this answer not relevant for #jaysonpryde, but maybe will help someone else.. :)

Related

Django PIPE youtube-dl to view for download

TL;DR: I want to pipe the output of youtube-dl to the user's browser on a button click, without having to save the video on my server's disk.
So I'm trying to have a "download" button on a page (django backend) where the user is able to download the video they're watching.
I am using the latest version of youtube-dl.
In my download view I have this piece of code:
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
file = ydl.download([f"https://clips.twitch.tv/{pk}"])
And it works, to some extend. It does download the file to my machine, but I am not sure how to allow users to download the file.
I thought of a few ways to achieve this, but the only one that really works for me would be a way to pipe the download to user(client) without needing to store any video on my disk. I found this issue on the same matter, but I am not sure how to make it work. I successfully piped the download to stdout using ydl_opts = {'outtmpl': '-'}, but I'm not sure how to pipe that to my view's response. One of the responses from a maintainer mentions a subprocess.Popen, I looked it up but couldn't make out how it should be implemented in my case.
I did a workaround.
I download the file with a specific name, I return the view with HttpResponse, with force-download content-type, and then delete the file using python.
It's not what I originally had in mind, but it's the second best solution that I could come up with. I will select this answer as accepted solution until a Python wizard gives a solution to the original question.
The code that I have right now:
def download_clip(request, pk):
ydl_opts = {
'outtmpl': f"{pk}.mp4"
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([f"https://clips.twitch.tv/{pk}"])
path = f"{pk}.mp4"
file_path = os.path.join(path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/force-download")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
os.remove(file_path)
return response
raise Http404

read text file content with python at zapier

I have problems getting the content of a txt-file into a Zapier
object using https://zapier.com/help/code-python/. Here is the code I am
using:
with open('file', 'r') as content_file:
content = content_file.read()
I'd be glad if you could help me with this. Thanks for that!
David here, from the Zapier Platform team.
Your code as written doesn't work because the first argument for the open function is the filepath. There's no file at the path 'file', so you'll get an error. You access the input via the input_data dictionary.
That being said, the input is a url, not a file. You need to use urllib to read that url. I found the answer here.
I've got a working copy of the code like so:
import urllib2 # the lib that handles the url stuff
result = []
data = urllib2.urlopen(input_data['file'])
for line in data: # file lines are iterable
result.append(line) # keep each line, or parse, etc.
return {'lines': result}
The key takeaway is that you need to return a dictionary from the function, so make sure you somehow squish your file into one.
​Let me know if you've got any other questions!
#xavid, did you test this in Zapier?
It fails miserably beacuse urllib2 doesn't exist in the zapier python environment.

How do I parse a file sent with other data from a multipart HTML form?

My server is uWSGI and Python. I send myself an image from a file upload on the web page. How do I parse that file on the server?
I was able to handle a CSV because it's just text and I sent it by itself, but I have no idea how to handle images, or if I send the text file with other data. I'll add sample POST data to clarify when I'm back at my computer.
Part of my problem is the previous developer did some weird things with parsing POST data, so instead of being able to let uWSGI turn it into usable data, I have to do that myself in Python.
I assume you were handeling url encoded data by doing read on environ['wigs.imput'], something like this.
try:
request_body_size = int(environ.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
request_body = environ['wsgi.input'].read(request_body_size)
dP = parse_qs(request_body)
For multipart/form-data data you need to use cgi.FieldStorage.
d = cgi.FieldStorage(environ=environ, fp=environ['wsgi.input'], keep_blank_values=True)
For Normal values in your form you can do
firstName = d.getvalue("firstName")
For the file you can get it by
file_data = d['imageFile'].file.read()
filename = d['imageFile'].filename

Django - Custom header to application/x-zip-compressed HttpResponse

I have a Django App which contains a view that returns a .zip file using HttpResponse
resp = HttpResponse(s.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
return resp
The .zip is created inside the view where I also calculate its checksum.
On the client-side I use a requests.get() to get the zip file.
How can I send the checksum in the same HttpResponse along with the zip.
I tried adding the checksum to the header by
resp['hash-key'] = sha_checksum
but on the client requests.headers['hash-key'] seems to be None
How can I do this?
Edit:
As it seems my problem is located in the calculation of the hash which results in None.
The strange thing is that the same function is used on the client-side and works fine, but I guess this is another question.
Since hash-key is in the response, it sounds like this line is working.
resp['hash-key'] = sha_checksum
Try printing the value of sha_checksum before you assign it, to make sure it is not None.

Testing Image upload with Django and Webtest

Does anyone know how I can test the image upload with using WebTest. My current code is:
form['avatar'] =('avatar', os.path.join(settings.PROJECT_PATH, 'static', 'img', 'avatar.png'))
res = form.submit()
In the response I get the following error "Upload a valid image. The file you uploaded was either not an image or a corrupted image.".
Any help will be appreciated.
Power was right. Unfortunately(or not) I found his answer after I spend half an hour debugging the webtest. Here is a bit more information.
Trying to pass only the path to the files brings you the following exception:
webtest/app.py", line 1028, in _get_file_info
ValueError: upload_files need to be a list of tuples of (fieldname,
filename, filecontent) or (fieldname, filename); you gave: ...
The problem is that is doesn't told you that it automatically will append the field name to the tuple send and making 3 item tuple into 4 item one. The final solutions was:
avatar = ('avatar',
file(os.path.join(settings.PROJECT_PATH, '....', 'avatar.png')).read())
Too bad that there is not decent example but I hope this will help anyone else too )
Nowadays selected answer didn't help me.
But I found the way to provide expected files in the .submit() method args
form.submit(upload_files=[('avatar', '/../file_path.png')])
With Python 3:
form["avatar"] = ("avatar.png", open(os.path.join(settings.PROJECT_PATH, '....', 'avatar.png', "rb").read())