I have been trying to follow different posts on downloading a file sent from my Web API. So far I can get the file to come, it will open the download window and it will save. However, I cannot open it so something must be wrong somewhere.
Here is my AngularJS so far.
return $http({
url: State.Endpoint + "/api/account/picture",
method: "GET",
responseType: 'arrayBuffer'
}).then(function (data) {
var octetStreamMime = 'application/octet-stream';
var success = false;
var file = new Blob([data.data], {
type: "image/jpeg"
});
var fileUrl = URL.createObjectURL(file);
var a = document.createElement('a');
a.href = fileUrl;
a.target = "_blank";
a.download = "myFile.jpg";
document.body.appendChild(a);
a.click();
});
That will make my successfully download the image for me. However, this doesn't let me open the file so either something is still wrong on client side or server side.
Server Side Code:
[Route("picture")]
[HttpGet]
public HttpResponseMessage GetPictureBlob()
{
HttpResponseMessage response = null;
var localFilePath = HttpContext.Current.Server.MapPath("~/Content/Images/demo.jpg");
if (!File.Exists(localFilePath))
{
response = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
var fStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
// Serve the file to the client
response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(fStream)
};
response.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = Path.GetFileName(fStream.Name)
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
//response.Headers.Add("content-type", "application/octet-stream");
}
return response;
}
The provided value 'arrayBuffer' is not a valid enum value of type XMLHttpRequestResponseType.
Use arraybuffer all lowercase:
$http({
url: State.Endpoint + "/api/account/picture",
method: "GET",
//responseType: 'arrayBuffer'
//USE arraybuffer lowercase
responseType: 'arraybuffer'
//OR
//responseType: 'blob'
})
When the responseType is not valid, the XHR API defaults to decoding the response as UTF-8. This corrupts binary files such as JPEG images.
For more information, see MDN XHR Web API - responseType.
Creating a Download Button
Instead of creating a <a download></a> element with JavaScript DOM manipulation, consider using the AngularJS framework.
This is an example of a Download button that becomes active after the data is loaded from the server:
<a download="data_{{files[0].name}}" xd-href="data">
<button ng-disabled="!data">Download</button>
</a>
The xdHref Directive
app.module("myApp").directive("xdHref", function() {
return function linkFn (scope, elem, attrs) {
scope.$watch(attrs.xdHref, function(newVal) {
if (newVal) {
elem.attr("href", newVal);
}
});
};
});
The DEMO on PLNKR.
I've done the very same thing with this code, where:
data: Data received from server
format: data format, it must be one of https://developer.mozilla.org/en-US/docs/Web/API/Blob/type
name: your file's name
Code:
function downloadBlobFile(data, format, name) {
// format must be one of https://developer.mozilla.org/en-US/docs/Web/API/Blob/type
var file = new Blob([data], {type: 'application/' + format});
file.lastModified = new Date();
file.name = name + '.' + format.trim().toLowerCase();
// guarantee IE compatibility
if($window.navigator && $window.navigator.msSaveOrOpenBlob) {
$window.navigator.msSaveOrOpenBlob(file, file.name);
}
//other web browser
else {
/**
* Because this technology's specification has not stabilized, compatibility has been
* checked here: https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL#Browser_compatibility
*/
var fileURL = $window.URL.createObjectURL(file);
/* trick for downloading the file, borrowed from:
http://stackoverflow.com/questions/19327749/javascript-blob-filename-without-link
*/
var a = angular.element("<a style='display: none;'/>").attr("href", fileURL).attr("download", file.name);
angular.element(document.body).append(a);
a[0].click();
$window.URL.revokeObjectURL(fileURL);
a.remove();
}
}
var a = document.createElement("a"); //Create <a>
a.href = "data:image/png;base64," + ImageBase64;
a.download = "Image.png"; //File name Here
a.click(); //Downloaded file
Simplest way worked for me
Related
I am trying to download a zip file from a Laravel API using Angular JS. I do not believe the issue is from Laravel.
Basically when the response comes and the download trigger is made it does not know its a .zip file, however the file itself is good. But then when I manually add the .zip extension in Angular JS in the file name the browser advises its a corrupt file.
If I do not add the extension, it downloads fine, and then if i rename the file with no extension in Windows and change it to test.zip it works perfectly as a zip file. This is how I know the data is good.
I have tried arraybuffer responseType and blob. With blob I am getting the download trigger, with arraybuffer nothing is happening (including no console errors).
Here is my JS controller code:
vm.downloadSelectedFiles = function() {
vm.selectedFiles = [];
angular.forEach(vm.fileDownloadList, function(value,index) {
if(value==1) {
vm.selectedFiles.push(index);
}
});
Data.downloadSelectedFiles(vm.selectedFiles,vm.stationIDToLookUp)
.then(function (data) {
var url = $window.URL || $window.webkitURL;
vm.fileUrl = url.createObjectURL(data.data);
var a = document.createElement("a");
a.href = vm.fileUrl;
a.download = 'test.zip';
//a.download = 'test';
a.click();
}).catch(function (err) {
});
}
Here is my JS service code
downloadSelectedFiles: function downloadSelectedFiles(selectedFiles,stationID) {
var apiBase = apiUrl + 'download-selected-files';
var config = {
//responseType: 'arraybuffer'
responseType: 'blob'
};
var data = {
selectedFiles: selectedFiles,
stationID: stationID
}
return $http.post(apiBase, data, config);
}
And just in case there is something relevant about the response from the API. Here is my Laravel code
public function downloadSelectedFiles(PublishDataRequest $requestData) {
return response()->file(storage_path() . '/app/files/test.zip');
}
Try setting the MIME type to application/zip:
Data.downloadSelectedFiles(vm.selectedFiles,vm.stationIDToLookUp)
.then(function (response) {
var blob = response.data;
var zipBlob = new Blob([blob], { type: "application/zip" });
var url = $window.URL || $window.webkitURL;
vm.fileUrl = url.createObjectURL(zipBlob);
var a = document.createElement("a");
a.href = vm.fileUrl;
a.download = 'test.zip';
//a.download = 'test';
a.click();
}).catch(function (response) {
console.log("ERROR", response);
throw response;
});
I'm trying to download files from server using SuperAgent. Please find the code below.
downloadDocument(fileIdMongo) {
var request = require('superagent');
var apiBaseUrl = "api/downloadDoc";
var self = this;
var req = request.get(apiBaseUrl);
req.query({ id: fileIdMongo })
req.end(function(err, res) {
if (err) {
console.log("error ocurred");
} else {
var blob = new Blob([res.text], {
type: 'text/csv/jpeg/jpg/png/pdf/docx/doc;charset=utf8;'
});
var element = document.createElement('a');
document.body.appendChild(element);
element.download = "Capture.PNG";
element.href = window.URL.createObjectURL(blob);
element.style.display = '';
element.click();
}
});
}
I'm trying to get a .png file from the server. I tested server with PostMan rest client. I'm able to get the .png file. But the file is not visible when using SuperAgent.
Use the below line of code in the else part.
window.location= 'api/CommercialInvoice?item=' + item.id,'';
element.click();
This is my API for returning a PDF with multiple images . Now when I invoke this with url it perfectly downloads the PDF with images . For eg two pages with images .
[HttpGet]
public async Task<IHttpActionResult> Download(Guid customDocId)
{
byte[] responseContent = await Task.FromResult(FileNetApiClientFactory.Get(customDocId).DownloadDocument(customDocId, "pdf", true));
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(responseContent),
StatusCode = HttpStatusCode.OK,
};
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = string.Concat(customDocId.ToString(), ".pdf") };
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return ResponseMessage(response);
}
Now from angular I am using blob and FileSaver for saving the PDF . So when I download it . Then it just returns two pages but with no content. But it shows page 1 and page2 but they are blank.
Here is my angular code :
//saveAs method is from FileSaver.js
vm.download = function () {
documentService.download($scope.customDocumentId).then(function (fileData) {
var blob = new Blob([fileData], { type: 'application/pdf' });
saveAs(blob, $scope.customDocumentId + ".pdf");
}).catch(function () {
});
}
And the service :
function _download(customDocumentId) {
return Restangular
.one('customdocument', customDocumentId).one('download')
.get(null, { responseType: 'arraybuffer' });
}
Does anyone has any idea why is it returning the blank pages when saved with FileSaver, while with direct download it is perfectly fine with all content.
I had to change certain things in Restangular . It was the responseType had to be arrayBuffer or 'blob'. I haven't tried with arrayBuffer explicitly . The blob responsetype worked for me . The configurations were missing from restangular. So I made a little change in my service and voila ! It was working.
So the updated Service looks like this now . DocumentServicesRestangular is nothing but a factory wrapper with changed baseurl through RestangularConfigurer.
function _download(customDocumentId) {
return DocumentServicesRestangular.one('customdocument', customDocumentId).one('download')
.withHttpConfig({ responseType: 'blob' }).get();
}
Try Like this,
$scope.download = function() {
var a = document.createElement("a");
document.body.appendChild(a);
var requestParams=$scope.downloadObj;
a.style = "display: none";
sServices.doAPIRequest(Url)
.then(function(generateData) {
var file = new Blob([$scope.base64ToArrayBuffer(generateData)], {
type : 'application/pdf'
});
var fileName="Data";
var fileURL = URL.createObjectURL(file);
a.href = fileURL;
a.download = fileName;
a.click();
});
};
$scope.base64ToArrayBuffer=function(data)
{
var binaryString = window.atob(data);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes;
};
I fixed this issue by changing the $http.get call in my service, so that the response type was set in the config argument:
return $http.get(apiTarget, { responseType: 'blob' });
If you are using FileSaver then this is the right syntax
var data = new Blob([response], { type: 'application/pdf;charset=utf-8' });
FileSaver.saveAs(data, filename);
I have written a service, which allows me to upload a given file along with a json object to a given url. My code is:
(function() {
angular.module('petrofacApp')
.service("fileUpload", fileUpload);
function fileUpload() {
function uploadFileToUrl(file, uploadUrl, jsonRequest, callback){
var fd = new FormData();
fd.append('JsonRequestData', JSON.stringify(jsonRequest));
fd.append('file', file, file.name);
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
var percentComplete = (e.loaded / e.total) * 100;
console.log(percentComplete);
}
};
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
callback(xhr.response);
}
};
xhr.open('POST', uploadUrl, true);
xhr.send(fd);
}
return {
uploadFileToUrl: uploadFileToUrl
}
}
}());
My problem is- if I mention the onprogress event as
xhr.onprogress = function(e) {
if (e.lengthComputable) {
var percentComplete = (e.loaded / e.total) * 100;
console.log(percentComplete);
}
};
file gets uploaded without any problem, but onprogress event gets triggered only once, when the upload is finished.
I found out that, to avoid this problem I have to use upload.onprogress. So I changed my code to
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
var percentComplete = (e.loaded / e.total) * 100;
console.log(percentComplete);
}
};
When I execute this, I get error-
XMLHttpRequest cannot load http://192.168.xxx.xxx:6152/api/UploadFile. Response for preflight has invalid HTTP status code 405.
Please help me in solving this issue.
The problem was related to CORS. I got it fixed from the server side by installing "CORS" from Nuget and then adding the following code to WebApi.config file
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
This solved my problem.
I was wondering how I can save a file that is contained in a response from the server in angular ? (So that the file is automatically downloaded when the response arrives)
Edit :
I have a $http post method, and I get pdf data in the response. On success, I want to save the response data as a pdf file.
E. g :
$http({
method: 'POST',
url : 'theUrl',
data: //some array that is received
headers : //content type info
}
.success(function(response) { // I want to save the response as a pdf });
On angular 2... you can do:
import { saveAs } from 'browser-filesaver/FileSaver.js'
downloadFile(data: Response) {
var blob = new Blob([data], {type: 'application/x-tar'});
saveAs(blob, "report.tgz");
}
Using HTML5 FileSaver interface, this can be achieved:
https://github.com/eligrey/FileSaver.js/
Example solution:
//Call API to retrieve file stream using POST request
$http.post("URL", searchData, { responseType: 'arraybuffer' }).then(
response => {
//Download file from response
saveFileAs(response);
},
data => {
//raise error
}
);
function saveFileAs(response) {
var contentDisposition = response.headers("content-disposition");
//Retrieve file name from content-disposition
var fileName = contentDisposition.substr(contentDisposition.indexOf("filename=") + 9);
fileName = fileName.replace(/\"/g, "");
var contentType = response.headers("content-type");
var blob = new Blob([response.data], { type: contentType });
saveAs(blob, fileName);
}
You can't save the document as you don't have access to the users file system in a browser. You could send the URL of the pdf back, then trigger the browsers build in file save / open mechanism by adding a dummy iFrame to the body:
$http({
method: 'POST',
url : 'theUrl',
data: //some array that is received
headers : //content type info
}
.success(function (data) {
if ($('#iframe').length == 0) {
var $iframe = $('<iframe id="iframe" style="display: none"></iframe>');
$('body').append($iframe);
}
$('#iframe').attr('src', {{url to retrieve the file}})
})