Background: The server returns a zip file and Json data in a multipart response.
On the frontend, when I use just chunkBlob as the blob for downloading the zip file (response also contains Json data), it results in the error,
"Windows cannot open the folder. The Compressed (zipped) Folder '[root]' is invalid",
but when I use chunkBlob2 as the blob for downloading the zip file it opens correctly.
How can I use something like TextDecoder().decode() first, because I need to process the chunk first (separate the zip file from the Json data) before I make it a blob, or how can I make it work with TextDecoder().decode?
.then(response => {
...
// Read the response as a multipart response
const reader = response.body.getReader();
reader.read().then(function processResult(result) {
...
// Convert the received chunk to a string and create a blob
const chunkStr = new TextDecoder().decode(result.value);
const chunkBlob = new Blob([chunkStr], { type: "application/zip" }); console.log(chunkBlob);
// Convert the received chunk to a blob
const chunkBlob2 = new Blob([result.value], { type: "application/zip" }); console.log(chunkBlob2);
// Testing using blobs using either a string (chunkStr/chunkBlob) or a chunk (result.value/chunkBlob2) as it is, to see if TextDecoder is the issue
let url = window.URL.createObjectURL(chunkBlob);
let a = document.createElement('a'); a.href = url;
// a.download = "folderAuto.zip";
a.click();
window.URL.revokeObjectURL(url);
I have also tired to .encode before creating the blob, with no luck.
// Convert to encoded string first and create a blob
const chunkStrEncode = new TextDecoder().decode(result.value);
const chunkUint8Array = new TextEncoder().encode(chunkStrEncode);
const chunkBlobEncode = new Blob([chunkUint8Array], { type: "application/zip" });
console.log(chunkBlobEncode);
Note: chunkBlob and chunkBlobEncode comes to size: 16057 and chunkBlob2 comes to size: 9468
Sever-side code:
def downloadfromserver(request, folderName, folderPath):
# Multipart response
# Create the zip archive
shutil.make_archive(folderName, 'zip', folderPath)
# Generate a random dummy JSON response
dummy_data = {"foo": "bar"}
# Set a boundary string in the Json header to in turn spilt the response
boundary_string = "MyBoundaryString"
json_boundary = "--" + boundary_string
# Add the zip file to the response
with open(f'{folderName}.zip', 'rb') as f:
response = HttpResponse(f.read(), content_type='application/zip')
response['Content-Disposition'] = f'attachment; filename={folderName}.zip'
# Add the dummy JSON to the response
response.write(json_boundary + "rn")
response.write("Content-Type: application/jsonrn")
response.write(
"Content-Disposition: attachment; filename=dummy.jsonrnrn")
response.write(json.dumps(dummy_data))
# Delete zip file and folder
os.remove(f'{folderName}.zip')
shutil.rmtree(folderPath)
return response
Another question I have is, since the fetch request to the server ends up creating the zip file on the server (sent back in the response for the user to download), would it be better to break everything up into 2 fetches instead of having a multipart response for a zip file and Json data (though one fetch/response seems most efficient)?
The first request would send back the user details, any error messages (where certain files in the zip couldn’t be download) and a string of the zip file name on the server (the zip that was just created) where on the second automatic fetch after the first, the string of the zip file name is sent back to the server so that 1. the correct zip file can be sent to the frontend for the user to download and 2. so the zip file can be deleted/cleared off the server.
Thank you