I'm trying to zip varying amounts of files so that one zip folder can be served to the user instead of them having to click multiple anchor tags. I am using the System.IO.Compression namespace in asp.net core 3.1 to create the zip folder.
Here is the code I'm using to create the Zip folder.
public IActionResult DownloadPartFiles(string[] fileLocations, string[] fileNames)
{
List<InMemoryFile> files = new List<InMemoryFile>();
for (int i = 0; i < fileNames.Length; i++)
{
InMemoryFile inMemoryFile = GetInMemoryFile(fileLocations[i], fileNames[i]).Result;
files.Add(inMemoryFile);
}
byte[] archiveFile;
using (MemoryStream archiveStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (InMemoryFile file in files)
{
ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest);
using (Stream zipStream = zipArchiveEntry.Open())
{
zipStream.Write(file.Content, 0, file.Content.Length);
zipStream.Close();
}
}
archiveStream.Position = 0;
}
archiveFile = archiveStream.ToArray();
}
return File(archiveFile, "application/octet-stream");
}
The files I am trying to zip are stored remotely so I grab them with this block of code. The InMemoryFile is a class to group the file name and file bytes together.
private async Task<InMemoryFile> GetInMemoryFile(string fileLocation, string fileName)
{
InMemoryFile file;
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(fileLocation))
{
byte[] fileContent = await response.Content.ReadAsByteArrayAsync();
file = new InMemoryFile(fileName, fileContent);
}
return file;
}
The DownloadPartFiles method is called using Ajax. I grab the remote paths to the files and their respective names using javascript and pass them into the Ajax call.
function downloadAllFiles() {
let partTable = document.getElementById("partTable");
let linkElements = partTable.getElementsByTagName('a');
let urls = [];
for (let i = 0; i < linkElements.length; i++) {
urls.push(linkElements[i].href);
}
if (urls.length != 0) {
var fileNames = [];
for (let i = 0; i < linkElements.length; i++) {
fileNames.push(linkElements[i].innerText);
}
$.ajax({
type: "POST",
url: "/WebOrder/DownloadPartFiles/",
data: { 'fileLocations': urls, 'fileNames': fileNames },
success: function (response) {
var blob = new Blob([response], { type: "application/zip" });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "PartFiles.zip";
link.click();
window.URL.revokeObjectURL(blob);
},
failure: function (response) {
alert(response.responseText);
},
error: function (response) {
alert(response.responseText);
}
});
}
}
Now the issue I keep running into is that I can't open the zip folder within Windows 10. Every time I try to open the zip folder using Windows or 7-zip I get an error message that the folder can't be opened or the folder is invalid. I've tried looking at various similar issues on stackoverflow, ie Invalid zip file after creating it with System.IO.Compression, but still can't figure out why this is.
Could it be the encoding? I found that Ajax expects its responses to be encoded UTF-8 and when I view the zip file using notepad++ with UTF-8 I see that there are � characters indicating corruption.
Any thoughts on this would be helpful. Let me know if more information is needed.
If one of the corrupt zip files is needed I can provide that as well.
Edit:
I have since changed my method of receiving the byte array in javascript. I am using a XMLHttpRequest to receive the byte array.
var parameters = {};
parameters.FileLocations = urls;
parameters.FileNames = fileNames;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "/WebOrder/DownloadPartFiles/", true);
xmlhttp.setRequestHeader("Content-Type", "application/json");
xmlhttp.responseType = "arraybuffer";
xmlhttp.onload = function (oEvent) {
var arrayBuffer = xmlhttp.response;
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
var blob = new Blob([byteArray], { type: "application/zip" });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "PartFiles.zip";
link.click();
window.URL.revokeObjectURL(blob);
}
}
xmlhttp.send(JSON.stringify(parameters));
From what I read, Ajax is not the best for receiving byte arrays and binary data. With this method I was able to open one of the zip file with 7-zip, but not Windows, however, one of the files within the archive was showing as a size of 0KB and couldn't be opened. The other three files in the archive were fine. Other zip folders with different files could not be opened at all though.
After some time I found a post that was able to fix my issue, Create zip file from byte[]
From that post this is the revised method I'm using to create a zip folder with files in it.
public IActionResult DownloadPartFiles([FromBody] FileRequestParameters parameters)
{
List<InMemoryFile> files = new List<InMemoryFile>();
for (int i = 0; i < parameters.FileNames.Length; i++)
{
InMemoryFile inMemoryFile = GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i]).Result;
files.Add(inMemoryFile);
}
byte[] archiveFile = null;
using (MemoryStream archiveStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (InMemoryFile file in files)
{
ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Optimal);
using (MemoryStream originalFileStream = new MemoryStream(file.Content))
using (Stream zipStream = zipArchiveEntry.Open())
{
originalFileStream.CopyTo(zipStream);
}
}
}
archiveFile = archiveStream.ToArray();
}
return File(archiveFile, "application/octet-stream");
}
I still don't know why the previous method was having issues so if anyone knows the answer to that in the future I'd love to know.
Related
So here in C# code i am sending corgi to client which has corgiBabies. Using ClosedXml here.
var wbCorgiBabiesTemplate = new XLWorkbook();
var wsCoriBabiesAmendementTemplate = wbCorgiBabiesTemplate.Worksheets.Add(" Work Sheet Corgi baby Template");
wsCoriBabiesAmendementTemplate.Cell("A1").Value = "Corgi Parent";
wsCoriBabiesAmendementTemplate.Cell("B1").Value = "Corgi Child";
wsCoriBabiesAmendementTemplate.Cell("A2").Value = "Petunia";
wsCoriBabiesAmendementTemplate.Cell("B2").Value = "Khaleesi";
using (var ms = new MemoryStream())
{
wbCorgiBabiesTemplate.SaveAs(ms);
byte[] Corgibabies = ms.ToArray();
}
corgi.Corgibabies = System.Text.Encoding.UTF8.GetString(Corgibabies);
return corgi;
After that in Client i want to open corgibabies in excel sheet but the conversion here is wrong somewhere i think that excel sheet doesn't open correctly.
var fileName = 'CorgiBabies.xlsx';
dataAccessService.get('corgi')
.then(function(response) {
let utf8Encode = new TextEncoder();
var strBytes = utf8Encode.encode(response.corgiBabies);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var file = new Blob([strBytes], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
var fileURL = window.URL.createObjectURL(file);
a.href = fileURL;
a.download = fileName;
a.click();
})
Below what excel sheet gives me error in image
Assuming you're on .Net Core+ (otherwise you can find the System.Buffers Nuget package for .Net standard or framework), on server side try
using System.Buffers;
using System.Buffers.Text;
and insert
var outputBuffer = new Span<byte>();
var status = Base64.EncodeToUtf8(Corgibabies, outputBuffer, out var consumed, out var written);
// sanity check
// if (status != OperationStatus.Done) throw new Exception();`
// do the above just before replacing
// System.Text.Encoding.UTF8.GetString(Corgibabies);
// with
System.Text.Encoding.UTF8.GetString(outputBuffer);
Now I'm pretty certain that will ensure that the server responds with what the client should expect but I'm not set up to test the Javascript side of things (yet). In the meantime let me know if this helps you make progress.
PS1: the error in your original code was the implicit assumption that Corgibabies is an array containing the bytes of a UTF8 encoded string. It actually contains the raw bytes of what would normally be an .xlsx file on disk. What is needed is to make that into text (Base64 encoding) and ensure that text is UTF8. Obviously in the Javascript you need to do the reverse - UTF8 Base64 to binary, save to disk, open in Excel...
Instead of returning string as the Content, you can make it work with File.
public ActionResult Get()
{
var wbCorgiBabiesTemplate = new XLWorkbook();
var wsCoriBabiesAmendementTemplate = wbCorgiBabiesTemplate.Worksheets.Add(" Work Sheet Corgi baby Template");
wsCoriBabiesAmendementTemplate.Cell("A1").Value = "Corgi Parent";
wsCoriBabiesAmendementTemplate.Cell("B1").Value = "Corgi Child";
wsCoriBabiesAmendementTemplate.Cell("A2").Value = "Petunia";
wsCoriBabiesAmendementTemplate.Cell("B2").Value = "Khaleesi";
wbCorgiBabiesTemplate.SaveAs("new.xlsx");
var ms = new MemoryStream();
wbCorgiBabiesTemplate.SaveAs(ms);
ms.Position = 0;
var fileName = "CorgiBabies.xlsx";
return File(ms, "application/octet-stream", fileName);
}
Api call:
or
fetch('https://localhost:7135/api/downloadExcel')
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// the filename you want
a.download = 'CorgiBabies.xlsx';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch(() => alert('oh no!'));
Ref: git
ClosedXML has several extensions that will help you acheive what you need :
ClosedXML.Extensions.AspNet
ClosedXML.Extensions.Mvc
ClosedXML.Extensions.WebApi
You can install the appropriate extension for your project, to help give you a fast access to download the workbook. You can also save the file on disk, and pass the file link (path) to JavaScript, and continue your work on the file from JavaScript.
if you need to know how you would let the user download the file from ASP.NET,
then you can do this :
Simple workbook :
C#: ASP.NET MVC
[HttpGet]
public ActionResult Download(string fileName)
{
// create workbook
var workbook = new XLWorkbook();
var sheet = workbook.Worksheets.Add("Worksheet 1");
sheet.Cell("A1").Value = "A1";
sheet.Cell("B1").Value = "B1";
sheet.Cell("A2").Value = "A2";
sheet.Cell("B2").Value = "B2";
// get workbook bytes
byte[] workbookBytes;
using (var memoryStream = new MemoryStream())
{
workbook.SaveAs(memoryStream);
workbookBytes = memoryStream.ToArray();
}
return File(workbookBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
C#: ASP.NET Web Forms
public void Export(HttpResponse response, string fileName)
{
// create workbook
var workbook = new XLWorkbook();
var sheet = workbook.Worksheets.Add("Worksheet 1");
sheet.Cell("A1").Value = "A1";
sheet.Cell("B1").Value = "B1";
sheet.Cell("A2").Value = "A2";
sheet.Cell("B2").Value = "B2";
HttpResponse httpResponse = response;
httpResponse.Clear();
httpResponse.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
httpResponse.AddHeader("content-disposition", $"attachment;filename=\"{fileName}.xlsx\"");
using (var memoryStream = new MemoryStream())
{
workbook.SaveAs(memoryStream);
memoryStream.WriteTo(httpResponse.OutputStream);
}
httpResponse.End();
}
the above examples will directly download the file into the client device. However, if you want to pass the workbook bytes to the JavaScript, you will need to convert it to base64 string and pass it to the JavaScript like so :
var base64String = Convert.ToBase64String(workbookBytes);
Then from JavaScript decode it to Uint8Array :
/*
JavaScript
*/
// get base64 string array and decoded it
var data = atob(serverSideResult);
var array = new Array(data.length);
for (var i = 0; i < data.length; i++) {
array[i] = data.charCodeAt(i);
}
// final result
var dataUint8Array = new Uint8Array(array);
now you can work with dataUint8Array as normal Uint8Array.
if you want to pass it back to the server-side, you can convert the array to base64 string, and pass it to the server-side like so :
/*
JavaScript
*/
let binaryString = ''
for (var i = 0; i < dataUint8Array.byteLength; i++) {
binaryString += String.fromCharCode(dataUint8Array[i]);
}
//pass base64Result to the server-side (C#)
var base64Result = window.btoa(binaryString);
then from C# you just need to convert it back to array from base64 string like so :
var bytes = Convert.FromBase64String(dataReceivedFromJavaScript);
where bytes would be byte[].
The scenario:
filter a jquery datatable according to our need.
use download button present besides page length dropdown(its a simple button not a submit button). to download the images for the filtered data.
get the record ids from the table, make its array and send it to the controller.
In controller, fetch the file paths from the database associated with the record ids sent from the
ajax req.
get the files, make a zip and send it back (in response) to the view (download).
as I mentioned the scenario I want the zip to get downloaded on the machine.
But the file is not getting downloaded.
--------------------------Edit [solution]:---------------------------
After Trying Many Solutions I finally got the solution.
So first, I used Controller Code as :
[HttpPost]
public ActionResult Ajax_DownloadImages(int[] records)
{
#region Variable Declaration
List<tbl_image_Details> obj_records = new List<tbl_image_Details>();
tbl_image_Details singleRecord = new tbl_image_Details();
var memorystream = new MemoryStream();
int temp = 0;
#endregion
using (Symphony_webServer_DBEntities db = new Symphony_webServer_DBEntities())
{
#region Get File paths from the database.
for (int i = 0; i < records.Count(); i++)
{
temp = records[i];
singleRecord = db.tbl_image_Details.Where(x => x.record_id == temp).FirstOrDefault<tbl_image_Details>();
obj_records.Add(singleRecord);
}
#endregion
#region Zipping and sending the data to download.
using (ZipFile obj_Zip = new ZipFile())
{
obj_Zip.AlternateEncodingUsage = ZipOption.AsNecessary;
obj_Zip.AddDirectoryByName("Images");
foreach (var file in obj_records)
{
obj_Zip.AddFile(file.image_path, "Images");
}
Response.ClearContent();
Response.ClearHeaders();
Response.AppendHeader("content-disposition", "attachment; filename=Myzip.zip");
obj_Zip.Save(memorystream);
}
memorystream.Position = 0;
return new FileStreamResult(memorystream, "application/octet-stream");
#endregion
}
}
The View:
Create a simple button and call the download function on its click event as follows.
Function That requests the zipped data from server is-
Note : I used XMLHttpRequest object to make a request call because,
jquery ajax call is not efficient to handle the blob response content.
funtion DownloadImages(){
// selecting the table
var Displayedtable = $("#recordTable").DataTable();
// fetching the rows of the table
var datatable_rows = Displayedtable.rows().data().toArray();
// creating an array to hold data.
var table_data = new Array();
// fetching data from each cell and putting it into the array.
$.each(datatable_rows, function (index, value) {
table_data.push(value['record_id']);
});
var records = JSON.stringify(table_data);
var ajax = new XMLHttpRequest();
ajax.open("Post", "/ReportsPage/Ajax_DownloadImages", true);
ajax.setRequestHeader("Content-Type", "application/json");
ajax.responseType = "blob";
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
var blob = new Blob([this.response], { type: "application/octet-stream" });
console.log(this.response);
alert(this.response);
var fileName = "Myzip.zip";
saveAs(blob, fileName);
}
};
ajax.send(records);
}
This will definitely download the intended zip file.
I have an api which return below response, which contain the excel file content.
So now I need to convert them into excel file and download for the user.
Here is the api function
[HttpGet]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> DownloadLoadedTrnFile(string S3Path)
{
try
{
string bucket = "taurus-" + GetEnvironmentSettings() + "-trn";
string fileName = "";
string[] fileStr = S3Path.Split('-');
if (fileStr.Count() > 0)
{
fileName = fileStr.Last();
}
Stream responseStream = await _imageStore.GetImage(bucket, S3Path);
if (responseStream == null)
return NotFound();
using (MemoryStream ms = new MemoryStream())
{
responseStream.CopyTo(ms);
var finalResult = File(System.Text.UTF8Encoding.UTF8.GetString(ms.ToArray()), MimeTypesMap.GetMimeType(S3Path), fileName);
return Ok(finalResult);
}
}
catch (Exception ex)
{
return StatusCode(500, "Error in downloading file.");
}
}
public async Task<Stream> GetImage(string bucketName, string objectKey)
{
GetObjectRequest originalRequest = new GetObjectRequest
{
BucketName = bucketName,
Key = objectKey
};
try
{
GetObjectResponse response = await S3Client.GetObjectAsync(originalRequest);
// AWS HashStream doesn't support seeking so we need to copy it back to a MemoryStream
MemoryStream outputStream = new MemoryStream();
response.ResponseStream.CopyTo(outputStream);
outputStream.Position = 0;
return outputStream;
}
catch (AmazonS3Exception ex)
{
// Not found if we get an exception
return null;
}
}
I have such function in the front-end as below,
function saveTextAsFile(data, filename, contentType) {
if (!data) {
console.error('Console.save: No data')
toastr.error("No data received from server");
return;
}
if (!filename) filename = 'noname.xlsx';
var blob = new Blob([s2ab(atob(data))], {
type: contentType
});
var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
}
and function
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
This function is working fine with excel that only has normal text. However, this excel i am trying to download, it has rich content such as color border, dropdown, multiple sheets.
When I try to use this same function to download the excel file, it throw me this error:
To help you more understand my problem, here is t he API HTTP CAll
I have try to search solution online but there is no luck. I actually do not understand what is the problem here. Anything will help thanks.
Thanks for all the replies and make my head around a little bit finally I fixed this issue.
Well, found the problem with it is because in the API I wrap that inside UTF-8. however, it (Excel) shouldn't be wrapped in UTF-8. only If I was downloading a csv file.
var finalResult = File(System.Text.UTF8Encoding.UTF8.GetString(ms.ToArray()),
MimeTypesMap.GetMimeType(S3Path), fileName);
Changed to
var finalResult = File(ms.ToArray(), MimeTypesMap.GetMimeType(S3Path), fileName);
Hi I have a web api like in the below picture - returning HttpResponseMessage, I am retutning fileStream as content of it, when I am trying to retrieve or log it on console as console.log(response.data)
it shows some other information but not the stream information or array or anything of that sort. Can somebody please help me how can I read or download file that's returned as stream or FileStream using React.
I am not using jQuery. Any help please, a link or something of that sort. I could able to download the file using byte array but need to implement using FileStream, any help please?
[EnableCors("AnotherPolicy")]
[HttpPost]
public HttpResponseMessage Post([FromForm] string communityName, [FromForm] string files) //byte[]
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var removedInvalidCharsFromFileName = removeInvalidCharsFromFileName(files);
var tFiles = removedInvalidCharsFromFileName.Split(',');
string rootPath = Configuration.GetValue<string>("ROOT_PATH");
string communityPath = rootPath + "\\" + communityName;
byte[] theZipFile = null;
FileStreamResult fileStreamResult = null;
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (string attachment in tFiles)
{
var zipEntry = zip.CreateEntry(attachment);
using (FileStream fileStream = new FileStream(communityPath + "\\" + attachment, FileMode.Open))
using (Stream entryStream = zipEntry.Open())
{
fileStream.CopyTo(entryStream);
}
}
}
theZipFile = zipStream.ToArray();
fileStreamResult = new FileStreamResult(zipStream, "application/zip") { FileDownloadName = $"{communityName}.zip" };
var i = zipStream.Length;
zipStream.Position = 0;
var k= zipStream.Length;
result.Content = new StreamContent(zipStream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/zip");
}
//return theZipFile;
return result;
}
Finally implemented by using the FileStreamResult as well maybe some people would be needed this, here is my API code and then I made call to the post method using axios, so here is my React code. In the axios call responseType becomes arraybuffer and the in the blob declaration it becomes the application/octet-stream type, Hence it completes everything, as I have imported the file-saver, I could able to use saveAs method of it. Finally after many efforts and hearing the screaming from PM, yes it is achieved - but that's the life of any Software Programmer.
Here is Web Api code C#:
[EnableCors("AnotherPolicy")]
[HttpPost]
public FileStreamResult Post([FromForm] string communityName, [FromForm] string files) //byte[]
{
var removedInvalidCharsFromFileName = removeInvalidCharsFromFileName(files);
var tFiles = removedInvalidCharsFromFileName.Split(',');
string rootPath = Configuration.GetValue<string>("ROOT_PATH");
string communityPath = rootPath + "\\" + communityName;
MemoryStream zipStream = new MemoryStream();
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (string attachment in tFiles)
{
var zipEntry = zip.CreateEntry(attachment);
using (FileStream fileStream = new FileStream(communityPath + "\\" + attachment, FileMode.Open))
{
using (Stream entryStream = zipEntry.Open())
{
fileStream.CopyTo(entryStream);
}
}
}
}
zipStream.Position = 0;
return File(zipStream, "application/octet-stream");
}
Then my client side React code is here:
handleDownload = (e) => {
e.preventDefault();
var formData = new FormData();
formData.append('communityname', this.state.selectedCommunity);
formData.append('files', JSON.stringify(this.state['checkedFiles']));
//let env='local';
let url = clientConfiguration['filesApi.local'];
//let tempFiles = clientConfiguration[`tempFiles.${env}`];
//alert(tempFiles);
axios({
method: 'post',
responseType: 'arraybuffer', //Force to receive data in a Blob Format
url: url,
data: formData
})
.then(res => {
let extension = 'zip';
let tempFileName = `${this.state['selectedCommunity']}`
let fileName = `${tempFileName}.${extension}`;
const blob = new Blob([res.data], {
type: 'application/octet-stream'
})
saveAs(blob, fileName)
})
.catch(error => {
console.log(error.message);
});
};
this event is called when button is clicked or form submitted. Thanks for all the support the SO has given - thanks a lot.
Before somebody says, "duplicate", I just want to make sure, that folks know, that I have already reviewed these questions:
1) Uses angular and php, not sure what is happening here (I don't know PHP): Download zip file and trigger "save file" dialog from angular method
2) Can't get this answer to do anything: how to download a zip file using angular
3) This person can already download, which is past the point I'm trying to figure out:
Download external zip file from angular triggered on a button action
4) No answer for this one:
download .zip file from server in nodejs
5) I don't know what language this even is:
https://stackoverflow.com/questions/35596764/zip-file-download-using-angularjs-directive
Given those questions, if this is still a duplicate, I apologize. Here is, yet, another version of this question.
My angular 1.5.X client gives me a list of titles, of which each have an associated file. My Node 4.X/Express 4.X server takes that list, gets the file locations, creates a zip file, using express-zip from npm, and then streams that file back in the response. I then want my client to initiate the browser's "download a file" option.
Here's my client code (Angular 1.5.X):
function bulkdownload(titles){
titles = titles || [];
if ( titles.length > 0 ) {
$http.get('/query/bulkdownload',{
params:{titles:titles},
responseType:'arraybuffer'
})
.then(successCb,errorCb)
.catch(exceptionCb);
}
function successCb(response){
// This is the part I believe I cannot get to work, my code snippet is below
};
function errorCb(error){
alert('Error: ' + JSON.stringify(error));
};
function exceptionCb(ex){
alert('Exception: ' + JSON.stringify(ex));
};
};
Node (4.X) code with express-zip, https://www.npmjs.com/package/express-zip:
router.get('/bulkdownload',function(req,resp){
var titles = req.query.titles || [];
if ( titles.length > 0 ){
utils.getFileLocations(titles).
then(function(files){
let filename = 'zipfile.zip';
// .zip sets Content-Type and Content-disposition
resp.zip(files,filename,console.log);
},
_errorCb)
}
});
Here's my successCb in my client code (Angular 1.5.X):
function successCb(response){
var URL = $window.URL || $window.webkitURL || $window.mozURL || $window.msURL;
if ( URL ) {
var blob = new Blob([response.data],{type:'application/zip'});
var url = URL.createObjectURL(blob);
$window.open(url);
}
};
The "blob" part seems to work fine. Checking it in IE's debugger, it does look like a file stream of octet information. Now, I believe I need to get that blob into the some HTML5 directive, to initiate the "Save File As" from the browser. Maybe? Maybe not?
Since 90%+ of our users are using IE11, I test all of my angular in PhantomJS (Karma) and IE. When I run the code, I get the old "Access is denied" error in an alert window:
Exception: {"description":"Access is denied...<stack trace>}
Suggestions, clarifications, answers, etc. are welcome!
Use this one:
var url="YOUR ZIP URL HERE";
window.open(url, '_blank');
var zip_file_path = "" //put inside "" your path with file.zip
var zip_file_name = "" //put inside "" file name or something
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = zip_file_path;
a.download = zip_file_name;
a.click();
document.body.removeChild(a);
As indicated in this answer, I have used the below Javascript function and now I am able to download the byte[] array content successfully.
Function to convert byte array stream (type of string) to blob object:
var b64toBlob = function(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
};
An this is how I call this function and save the blob object with FileSaver.js (getting data via Angular.js $http.get):
$http.get("your/api/uri").success(function(data, status, headers, config) {
//Here, data is type of string
var blob = b64toBlob(data, 'application/zip');
var fileName = "download.zip";
saveAs(blob, fileName);
});
Note: I am sending the byte[] array (Java-Server-Side) like this:
byte[] myByteArray = /*generate your zip file and convert into byte array*/ new byte[]();
return new ResponseEntity<byte[]>(myByteArray , headers, HttpStatus.OK);
I updated my bulkdownload method to use $window.open(...) instead of $http.get(...):
function bulkdownload(titles){
titles = titles || [];
if ( titles.length > 0 ) {
var url = '/query/bulkdownload?';
var len = titles.length;
for ( var ii = 0; ii < len; ii++ ) {
url = url + 'titles=' + titles[ii];
if ( ii < len-1 ) {
url = url + '&';
}
}
$window.open(url);
}
};
I have only tested this in IE11.