Check this answer here - it does not require a filename, and I'll bet its much easier to use. I've tried quite a few javascript compression/decompression implementations and have been stung by problems such as limits on size of original data, overall speed, efficiency, and so forth. It is oddly difficult to find a good compression/decompression implementation in javascript, but thankfully this one hasn't failed me yet (and I've used it quite a bit):
Compressing a blob in javascript
The implementation you have currently requires the filename because it is trying to be consistent with the zip, so that you can save it for example to a desktop and open it with your favorite zip utility. It sounds like your challenge is very similar to mine, I needed to save and restore compressed items out of local storage in the browser as well as on the server.
Answer from Matt Mullens on Stack Overflowfile - javascript - zip a Blob with zip.js - Stack Overflow
How to save binary data of zip file in Javascript? - Stack Overflow
c# - generate a Zip file from azure blob storage files - Stack Overflow
Creating a Zip File from Blob Storage Using Python in Azure Databricks
Check this answer here - it does not require a filename, and I'll bet its much easier to use. I've tried quite a few javascript compression/decompression implementations and have been stung by problems such as limits on size of original data, overall speed, efficiency, and so forth. It is oddly difficult to find a good compression/decompression implementation in javascript, but thankfully this one hasn't failed me yet (and I've used it quite a bit):
Compressing a blob in javascript
The implementation you have currently requires the filename because it is trying to be consistent with the zip, so that you can save it for example to a desktop and open it with your favorite zip utility. It sounds like your challenge is very similar to mine, I needed to save and restore compressed items out of local storage in the browser as well as on the server.
The filename is necessary according to this implementation. It wouldn't be necessary if you were only compressing the data, but zip.js builds zip files, which store files, which must have filenames.
In your original example, zipWriter.add() effectively converts your blob into a new file and adds it to a zip – and the "filename" parameter is the name you want that new file to have.
Here's an example that uses zip.js to add multiple blobs to a zip and then downloads it with FileSaver.js:
function zipBlob() {
zip.createWriter(new zip.BlobWriter("application/zip"), function(writer) {
files = ["abcd", "123"];
var f = 0;
function nextFile(f) {
fblob = new Blob([files[f]], { type: "text/plain" });
writer.add("file"+f, new zip.BlobReader(fblob), function() {
// callback
f++;
if (f < files.length) {
nextFile(f);
} else close();
});
}
function close() {
// close the writer
writer.close(function(blob) {
// save with FileSaver.js
saveAs(blob, "example.zip");
});
}
nextFile(f);
}, onerror);
}
Finally I got answer of my question:
- https://github.com/eligrey/Blob.js/
- https://github.com/eligrey/FileSaver.js/
Here is the code:
var xhr = new XMLHttpRequest();
xhr.open("POST", baseURLDownload + "/service/report/QCPReport", true);
xhr.setRequestHeader("Content-type","application/json");
xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
// alert("Failed to download:" + xhr.status + "---" + xhr.statusText);
var blob = new Blob([xhr.response], {type: "octet/stream"});
var fileName = "QCPReport.zip";
saveAs(blob, fileName);
}
}
xhr.responseType = "arraybuffer";
xhr.send(JSON.stringify(QCPParameter));
Axios implementation:
const url = 'https://www.example.com/download-zip'
// Payload, eg list of docs to zip
const payload = { documents: ['id1', 'id2', 'id3'] }
// Axios options
const axiosOptions = {
responseType: 'arraybuffer',
headers: {
'Content-Type': 'application/json'
}
}
// Fetch data and save file
axios
.post(url, payload, axiosOptions)
.then((response) => {
const blob = new Blob([response.data], {
type: 'application/octet-stream'
})
const filename = 'download.zip'
saveAs(blob, filename)
})
.catch((e) => {
// Handle error
})
})
Note
saveAs is a function from file-saver package I've been using to handle saving the file.
We have solved this problem (partially) by zipping the files directly to the output stream using the blob streams. This avoids the issue of downloading zipping then sending and avoids the delay while this happens (we used ICSharpZipLib, reference). But it still means routing the stream through the web server:
public void ZipFilesToResponse(HttpResponseBase response, IEnumerable<Asset> files, string zipFileName)
{
using (var zipOutputStream = new ZipOutputStream(response.OutputStream))
{
zipOutputStream.SetLevel(0); // 0 - store only to 9 - means best compression
response.BufferOutput = false;
response.AddHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.ContentType = "application/octet-stream";
foreach (var file in files)
{
var entry = new ZipEntry(file.FilenameSlug())
{
DateTime = DateTime.Now,
Size = file.Filesize
};
zipOutputStream.PutNextEntry(entry);
storageService.ReadToStream(file, zipOutputStream);
response.Flush();
if (!response.IsClientConnected)
{
break;
}
}
zipOutputStream.Finish();
zipOutputStream.Close();
}
response.End();
}
The storage service simply does this:
public void ReadToStream(IFileIdentifier file, Stream stream, StorageType storageType = StorageType.Stored, ITenant overrideTenant = null)
{
var reference = GetBlobReference(file, storageType, overrideTenant);
reference.DownloadToStream(stream);
}
private CloudBlockBlob GetBlobReference(IFileIdentifier file, StorageType storageType = StorageType.Stored, ITenant overrideTenant = null)
{
var filepath = GetFilePath(file, storageType);
var container = GetTenantContainer(overrideTenant);
return container.GetBlockBlobReference(filepath);
}
Since blob storage is "just" an object store, you would need to download them somewhere (it could be a web/worker role or your local computer), zip them and then reupload the zip file. That's the only way to do it as far as I know.
If you're downloading a non text file (like a zip file) via ajax, you have to specify a responseType, a binary one, in the example below I set it as blob so that the data you receive in the ajax response will be a blob.
A blob url is created and used in the anchor instead of a bulky data uri.
$.ajax({
url:download.attr('href'),
cache:false,
xhrFields:{
responseType: 'blob'
},
success: function(data){
var blobUrl = window.URL.createObjectURL(data);
const anchor = document.createElement('a');
anchor.style.display = 'none';
anchor.href = blobUrl;
anchor.download = 'download.zip';
anchor.click();
hideLoading();
},
error:function(){
}
});
jQuery 3+ needed for this to work.
Just wanted to share this for anyone who wants to do this without using jQuery3... based on the answer posted by Musa: https://stackoverflow.com/a/60510567/302533
$('[download]').on('click', function (e) {
e.preventDefault();
showFullScreenLoading();
var $this = $(this);
var request = new XMLHttpRequest();
request.open('GET', $this.attr('href'), true);
request.responseType = 'blob';
request.onload = function (e) {
var data = request.response;
var blobUrl = window.URL.createObjectURL(data);
var downloadLink = document.createElement('a');
downloadLink.href = blobUrl;
downloadLink.download = $this.attr('download') || 'download';
downloadLink.click();
hideFullscreenLoading();
};
request.send();
});
I have a case where I need to provide a zip file to download from browser from the multiple files in the Azure blob storage?
I believe that this can't be done with out downloading the files to a machine and the pack it.
(I use Azure functions for API's, so downloading them to function memory is not kinda good & end zip file could be ~5 GB)
Are there any other alternatives to achieve this?