1

Currently I am making a call to an API an that sends back a zip file. When using requests

res = requests.post('http://url/to/api', files={'file_pro': *my_file*})

I am able to get a successful response with the returned zip as a string. When I examine the contents of my res.contents I get :

PK\x03\x04\x14\x00\x00\x00\x08\x00\x0c\x83HH\xba\xd2\xf1\t\xa1\x00\x00\x00\x04\x01\x00\x00....05\x06\x00\x00\x00\x00\x08\x00\x08\x00\x1e\x02\x00\x00$\x04\x00\x00\x00\x00

It looks like its returning the zip file as a string. I looked at this question here to attempt to transform this string into the original zip file. Specifically I wrote:

my_file = UploadedFile(file=File(zipfile.ZipFile(StringIO.StringIO(res.content)),'r'))
my_file.save()

Upon trying to save I get the following error:

KeyError: 'There is no item named 65536 in the archive'

My ultimate goal is to bind this zip file to an UploadedFile

class UploadedFile(BaseModel):
  file = models.FileField(upload_to='/path', max_length=255)

If I use a html form to reach this API my browser automatically downloads the zip after the request is successful. Any idea how to fix this?

Community
  • 1
  • 1
theamateurdataanalyst
  • 2,794
  • 4
  • 38
  • 72

1 Answers1

1

The requests library allows you get the response back as binary data - http://docs.python-requests.org/en/master/user/quickstart/#binary-response-content.

You don't need to use ZipFile to reconstruct the zip, the data passed back should already be the bytes of a zip file.

from django.core.files.uploadedfile import SimpleUploadedFile

# res.content is the bytes of your API response
res = request.post('http://url/to/api', files={'file_pro': *myfile*})
my_file = SimpleUploadedFile('temp.zip', res.content)

# verify the zip file
assert zipfile.is_zipfile(my_file)

# finally save the file
uploaded_file = UploadedFile(file=my_file)
uploaded_file.save()

# play around with the zipfile
with zipfile.ZipFile(uploaded_file.file) as my_zip_file:
    print(my_zip_file.infolist())

Note that zipfile.ZipFile takes either a filename or a file-like object. In your question, you passed a string/bytes directly into it.

Also consider renaming the UploadedFile model, as Django already has one built-in at django.core.files.uploadedfile.UploadedFile.

Derek Kwok
  • 12,768
  • 6
  • 38
  • 58
  • Thanks for the answer. It makes sense. The part of a confused about is your use of ```SimpleUploadedFile``` There's not a lot of documentation on this object. Is there any other way to do this without having to to use SimpleUploadedFile? – theamateurdataanalyst Feb 09 '16 at 07:26
  • A `SimpleUploadedFile` is a subclass of `File` in Django (which you used in your question) and the implementation of it uses Python's `BytesIO`. If you don't want to use `SimpleUploadedFile`, you can achieve the same effect by doing `File(BytesIO(res.content))`. – Derek Kwok Feb 09 '16 at 15:17
  • I used ```ContentFile``` and that worked too. Thanks for your help. – theamateurdataanalyst Feb 09 '16 at 15:49