Download a zip file created entirely in memory with Django - django

I need to generate several csv reports, compress and serve as zip to the user. I'm using this snippet as reference
...
temp = StringIO.StringIO()
with zipfile.ZipFile(temp,'w') as archive:
for device in devices:
csv = Mymodel.get_csv_for(device)
archive.writestr('{}_device.csv'.format(device), str(csv))
response = HttpResponse(FileWrapper(temp), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename="devices.zip"')
return response
Looking at the archive.listname() i can see the file names.
Looking at temp.getvalue() i can see some string but when I download the file it comes out empty.

You need to call temp.seek(0) before returning the response, otherwise Python will try to read the memory file from its end (where you left it at after writing the archive to it) and therefore will not find any content and return an empty HTTP response.
You also need to use a StreamingHttpResponse instead of a HttpResponse.
That would give:
...
temp = StringIO.StringIO()
with zipfile.ZipFile(temp,'w') as archive:
for device in devices:
csv = Mymodel.get_csv_for(device)
archive.writestr('{}_device.csv'.format(device), str(csv))
response = StreamingHttpResponse(FileWrapper(temp), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename="devices.zip"')
response['Content-Length'] = temp.tell()
temp.seek(0)
return response

Related

Get files from Aw3 and append them in a .zip in Django

I would like to download some files from AWS S3 in a .zip, with all these files.
I am using django==2.2.1 with boto3 library, last version.
I am trying to get the files one by one, convert them with json.dump and insert them in a .zip file with generate_zip function.
Not resolve nothing, I mean, it not resolve any error and it not response the .zip or something in console that I would see. Probably the .zip was not created, but I do not know because, like I told before, it not response nothing.
My code is:
for id_file in files:
BUCKET_NAME="xxxx"
BUCKET_FILE_NAME='folder/'+id_file+'/'+category+"/"+name_file
s3 = boto3.client('s3',aws_access_key_id=settings.AWS_ACCESS_KEY_ID,aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,region_name='eu-west-2')
s3_response_object=s3.get_object(Bucket=BUCKET_NAME,Key=BUCKET_FILE_NAME)
s3_response_object['Body'] = s3_response_object['Body'].read().decode("ISO-8859-1")
s3_response_object['Key'] = settings.AWS_ACCESS_KEY_ID+'/'+settings.AWS_SECRET_ACCESS_KEY
location = s3.get_bucket_location(Bucket=BUCKET_NAME)['LocationConstraint']
url = "https://s3-%s.amazonaws.com/%s/%s" % (location, BUCKET_NAME, BUCKET_FILE_NAME)
s3_response_object['URL'] = url
file_data = json.dumps(s3_response_object, default = myconverter)
resultFiles.append(file_data)
full_zip_in_memory = generate_zip(resultFiles)
response = HttpResponse(full_zip_in_memory, content_type='application/x-zip')
response['Content-Disposition'] = 'attachment; filename="files.zip"'
return response
La funciĆ³n generate_zip
def generate_zip(files):
mem_zip = BytesIO()
with zipfile.ZipFile(mem_zip, mode="w",compression=zipfile.ZIP_DEFLATED) as zf:
for f in files:
zf.writestr(f[0], f[1])
return mem_zip.getvalue()

Django, Default file icon is missing after download of file

I have written code for downloading a file through an API. It works fine as I can see. The file size is the same. But the file has no longer a default file icon. I am pretty new at this and maybe I am doing something wrong. I am reading the file as I would for a standard textfile and saving it in the same way with the binary option. So how can the files be same size and still something seems to be missing in the downloaded file? Is there a better way to download files?
This is the code on the server:
file_location = 'static/File.pkg'
try:
with open(file_location, 'rb') as f:
filex_data = f.read()
response = HttpResponse(filex_data, content_type='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename="File.pkg"'
return response
This is the code on my local computer:
url = 'http://myServer/waprfile/'
x = requests.get(url, data=data, headers=headers)
f = open("TheNewFile.pgk", "ab")
f.write(x.content)
f.close()

How to download data from azure-storage using get_blob_to_stream

I have some files in my azure-storage account. i need to download them using get_blob_to_stream.it is returning azure.storage.blob.models.Blob object. so i couldn't download it by using below code.
def download(request):
file_name=request.POST['tmtype']
fp = open(file_name, 'wb')
generator = block_blob_service.list_blobs(container_name)
for blob in generator:
print(blob.name)
if blob.name==file_name:
blob=block_blob_service.get_blob_to_stream(container_name, blob.name, fp,max_connections= 2)
response = HttpResponse(blob, content_type="image/png")
response['Content-Disposition'] = "attachment; filename="+file_name
return response
You can actually use the get_blob_to_path property, below is an example in python:
from azure.storage.blob import BlockBlobService
bb = BlockBlobService(account_name='', account_key='')
container_name = ""
blob_name_to_download = "test.txt"
file_path ="/home/Adam/Downloaded_test.txt"
bb.get_blob_to_path(container_name, blob_name_to_download, file_path, open_mode='wb', snapshot=None, start_range=None, end_range=None, validate_content=False, progress_callback=None, max_connections=2, lease_id=None, if_modified_since=None, if_unmodified_since=None, if_match=None, if_none_match=None, timeout=None)
This example with download a blob file named: "test.txt", in a container, to File_path"/home/Adam/Downloaded_test.txt" , you can also keep the same name if you'd like to as well. You can find more samples including this one in https://github.com/adamsmith0016/Azure-storage
If you want to use get_blob_to_stream. You can download with below code:
with io.open(file_path, 'wb') as file:
blob = block_blob_service.get_blob_to_stream(
container_name=container_name,
blob_name=blob_name, stream=file,
max_connections=2)
Just note that the file content will be streamed to the file rather than the returned blob object. The blob.content should be None. That is by design. See https://github.com/Azure/azure-storage-python/issues/538.

Django create dynamic txt file and download with special file name

I am trying create dynamic text file and download it when my method called.I used steps at here.But when I change the file name, file isn't downloaded, displayed on browser. What can I do ? My code is below. Thanks for advice.
def view_method(request):
file_name = 'students.txt'
lines = []
data = Student.objects.all()
for d in data:
lines.append('{0};{1};{2}'.format(d.name,d.surname,d.amount))
response_content = '\n'.join(lines)
response = HttpResponse(response_content, content_type="text/plain,charset=utf8")
response['Content-Disposition'] = 'attachment; filename={0}'.format(file_name)
return response
I solved this problem with correct the file name chars.My file name contains utf-8 characters and points.

pytest-django how test excel response [duplicate]

Right now I'm just checking the response of the link like so:
self.client = Client()
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
Is there a Django-ic way to test a link to see if a file download event actually takes place? Can't seem to find much resource on this topic.
If the url is meant to produce a file rather than a "normal" http response, then its content-type and/or content-disposition will be different.
the response object is basically a dictionary, so you could so something like
self.assertEquals(
response.get('Content-Disposition'),
"attachment; filename=mypic.jpg"
)
more info:
https://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment
UPD:
If you want to read the actual contents of the attached file, you can use response.content. Example for a zip file:
try:
f = io.BytesIO(response.content)
zipped_file = zipfile.ZipFile(f, 'r')
self.assertIsNone(zipped_file.testzip())
self.assertIn('my_file.txt', zipped_file.namelist())
finally:
zipped_file.close()
f.close()