I provide a method on my WebApi to download a file:
public HttpResponseMessage Get(int id)
{
MemoryStream file = RetrieveFile(id);
if (file != null)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(file);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "dummy.txt"
};
return response;
}
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
This works fine and i can consume it with a WebClient.
I also tied to implement the javascript front-end using FileSaver.js :
$http({
url: myUrl,
method: 'GET',
params: {
id: 1
},
headers: {
'Content-type': 'application/json'
}
}).success(function (data, status, headers, config) {
var file = new Blob([data], {
type: 'application/octet-stream'
});
saveAs(file, "test.txt");
}).error(function (data, status, headers, config) {
});
This works great but it fails on IE9 since FileSaver and Blop is not supported.
I have tried opening the url in a new window but can't find a decent working solution for IE9
Related
I am working Angular SPA application.
I am using SP.js & SP.Requestor.js which is useful to upload the same but none seems to be working of Angular 4 App.
UploadFile(event: any) {
let fileList: FileList = event.target.files;
let fileName;
if (fileList.length != 0) {
this.getFileBuffer(fileList[0]).then(result => {
this.uploadFile(result, fileList[0].name, "POC").then(result => {
alert("added");
});
});
// this.fileUpload(fileList[0], "RetrievePOC", fileList[0].name);
}
}
getFileBuffer(file) {
return new Promise((resolve, reject) => {
var myReader: FileReader = new FileReader();
myReader.onload = function (e) {
resolve(myReader.result);
//resolve(e.target);
}
myReader.onerror = function (e) {
//deferred.reject(e.target.error);
}
//myReader.readAsArrayBuffer(file);
myReader.readAsBinaryString(file);
//resolve(file);
//return deferred.promise();
});
};
uploadFile(file, fileName, libraryName) {
return new Promise((resolve, reject) => {
// Construct the endpoint - The GetList method is available for SharePoint Online only.
//var serverRelativeUrlToFolder = "decodedurl='" + "/" + libraryName + "'";
var endpoint = this.siteUrl + "/_api/web/lists/getByTitle('POC')/Files/add('" + fileName + "')";
const headers = {
"accept": "application/json;odata=verbose",
"content-type": "application/json; odata=verbose",
};
//let fileData =this.convertDataBinaryString(file);
this.executeAsync(endpoint, file, headers).then(file => resolve(true)).catch(err => reject(err));
});
}
executeAsync(endPointUrl, data, requestHeaders) {
return new Promise((resolve, reject) => {
// using a utils function we would get the APP WEB url value and pass it into the constructor...
let executor = new SP.RequestExecutor(this.siteUrl);
// Send the request.
executor.executeAsync({
url: endPointUrl,
method: "POST",
body: data,
binaryStringRequestBody: true,
headers: requestHeaders,
success: offset => resolve(offset),
error: err => reject(alert(err.responseText))
});
});
}
Getting following error in executeAsync methond :
ErrorPage.PostMessage: Origin=https://localhost:44316,
Data={"command":"Query","postMessageId":"SP.RequestExecutor3","responseAvailable":false,"errorCode":-1007,"errorMessage":"Correlation
ID: e12d5a9e-b0d6-0000-745f-24b31dd971a6"}
vendor.js?v=T82_qgC1tKr4vAoag-4pr9ch_dUDSit3nEgaqP4H0Ec:12090 ERROR
Error: Uncaught (in promise): undefined
at resolvePromise (vendor.js?v=T82_qgC1tKr4vAoag-4pr9ch_dUDSit3nEgaqP4H0Ec:87020
)
We can use jQuery to upload file, here is a demo for your reference:
HTML:
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js" type="text/javascript"></script>
<input id="getFile" type="file"/><br />
<input id="displayName" type="text" value="Enter a unique name" /><br />
<input id="addFileButton" type="button" value="Upload" onclick="uploadFile()"/>
JS code:
'use strict';
jQuery(document).ready(function () {
// Check for FileReader API (HTML5) support.
if (!window.FileReader) {
alert('This browser does not support the FileReader API.');
}
});
// Upload the file.
// You can upload files up to 2 GB with the REST API.
function uploadFile() {
// Define the folder path for this example.
var serverRelativeUrlToFolder = '/shared documents';
// Get test values from the file input and text input page controls.
var fileInput = jQuery('#getFile');
var newName = jQuery('#displayName').val();
// Get the server URL.
var serverUrl = _spPageContextInfo.webAbsoluteUrl;
// Initiate method calls using jQuery promises.
// Get the local file as an array buffer.
var getFile = getFileBuffer();
getFile.done(function (arrayBuffer) {
// Add the file to the SharePoint folder.
var addFile = addFileToFolder(arrayBuffer);
addFile.done(function (file, status, xhr) {
// Get the list item that corresponds to the uploaded file.
var getItem = getListItem(file.d.ListItemAllFields.__deferred.uri);
getItem.done(function (listItem, status, xhr) {
// Change the display name and title of the list item.
var changeItem = updateListItem(listItem.d.__metadata);
changeItem.done(function (data, status, xhr) {
alert('file uploaded and updated');
});
changeItem.fail(onError);
});
getItem.fail(onError);
});
addFile.fail(onError);
});
getFile.fail(onError);
// Get the local file as an array buffer.
function getFileBuffer() {
var deferred = jQuery.Deferred();
var reader = new FileReader();
reader.onloadend = function (e) {
deferred.resolve(e.target.result);
}
reader.onerror = function (e) {
deferred.reject(e.target.error);
}
reader.readAsArrayBuffer(fileInput[0].files[0]);
return deferred.promise();
}
// Add the file to the file collection in the Shared Documents folder.
function addFileToFolder(arrayBuffer) {
// Get the file name from the file input control on the page.
var parts = fileInput[0].value.split('\\');
var fileName = parts[parts.length - 1];
// Construct the endpoint.
var fileCollectionEndpoint = String.format(
"{0}/_api/web/getfolderbyserverrelativeurl('{1}')/files" +
"/add(overwrite=true, url='{2}')",
serverUrl, serverRelativeUrlToFolder, fileName);
// Send the request and return the response.
// This call returns the SharePoint file.
return jQuery.ajax({
url: fileCollectionEndpoint,
type: "POST",
data: arrayBuffer,
processData: false,
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
"content-length": arrayBuffer.byteLength
}
});
}
// Get the list item that corresponds to the file by calling the file's ListItemAllFields property.
function getListItem(fileListItemUri) {
// Send the request and return the response.
return jQuery.ajax({
url: fileListItemUri,
type: "GET",
headers: { "accept": "application/json;odata=verbose" }
});
}
// Change the display name and title of the list item.
function updateListItem(itemMetadata) {
// Define the list item changes. Use the FileLeafRef property to change the display name.
// For simplicity, also use the name as the title.
// The example gets the list item type from the item's metadata, but you can also get it from the
// ListItemEntityTypeFullName property of the list.
var body = String.format("{{'__metadata':{{'type':'{0}'}},'FileLeafRef':'{1}','Title':'{2}'}}",
itemMetadata.type, newName, newName);
// Send the request and return the promise.
// This call does not return response content from the server.
return jQuery.ajax({
url: itemMetadata.uri,
type: "POST",
data: body,
headers: {
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
"content-type": "application/json;odata=verbose",
"content-length": body.length,
"IF-MATCH": itemMetadata.etag,
"X-HTTP-Method": "MERGE"
}
});
}
}
// Display error messages.
function onError(error) {
alert(error.responseText);
}
I want to download an excel from my angular controller using blob.
the server code of the mvc application is
[HttpGet]
public HttpResponseMessage Download(SearchModel search){
string fileName = "test.xls"
byte[] file = GetFile(search)
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new ByteArrayContent(file.ToArray());
httpResponseMessage.Content.Headers.Add("x-filename", fileName);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
return httpResponseMessage;
}
and the js
$http({ method: 'GET', url: 'Download', params: {...some params}, responseType: 'arrayBuffer' })
.success(function (data, status, headers) {
headers = headers();
var filename = headers['x-filename'];
var contentType = headers['content-type'];
var linkElement = document.createElement('a');
try {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(new Blob([data],{type: "application/octet-stream"}));
return;
}
var a = $("<a style='display: none;'/>");
var url = window.URL.createObjectURL(new Blob([data], {type: "application/octet-stream"}));
a.attr("href", url);
a.attr("download", "name");
$("body").append(a);
a[0].click();
window.URL.revokeObjectURL(url);
a.remove();
}
catch (ex) {
console.log(ex);
}
})
.error(function () { });
but there are some problems:
the filename in the javascript side is undefined and not test.xls as in the server side
the contentType is "text/html; charset=utf-8" and not "application/octet-stream" as in the server side
the file that is downloaded is this
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.ByteArrayContent, Headers:
{
x-filename: test.pdf
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=test.pdf
}
The file is a JSON with header information .
Why is this file wrong?
I'm trying to upload files using Angularjs for REST API exposed via Spring :
This is my Controller:
#RequestMapping(value = util/images/{partyId},
method = RequestMethod.POST)
public JsonNode uploadPartyRefImage(#RequestPart("file") MultipartFile inputFile,
#PathVariable String partyId){
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jNode = null;
try {
String[] fileInput = ((DiskFileItem) ((CommonsMultipartFile) inputFile).getFileItem()).getName().split("\\.");
Path storagePath= Paths.get(uploadPath);
FileSystemUtil.writeFile(storagePath,inputFile.getInputStream());
jNode = objectMapper.readTree("{\"type\":\"" + "success" + "\",\"fileStorePath\":\"" + pathString + "\"}");
} catch (Exception exApi) {
LOGGER.error("Error uploading Party attached Image "+ exApi);
}
return jNode;
}
This API works fine when used via Postman. The Postman call:
But when calling through angular it throws Exception:
Angular service:
function uploadImage(formData,partyRefId){
console.log(utilService);
if (utilService) {
return utilService.request({
method: "POST",
resource: "/images/" + partyRefId,
headers: { 'Content-Type': undefined},
processData: false,
transformRequest: angular.identity,
mimeType: "multipart/form-data",
cache: false,
data: formData
})
.then(function (data) {
if (data) {
return getResultDataFromResponse(data);
}
}, function (error) {
console.log('error invoking document upload service', error);
});
}
}
Angular Controller:
$ctrl.uploadDocument = function () {
var formData = new FormData();
formData.append("file", k.documentfile.lfFile);
var fileName = "PARTY01" + k.documentReference;
fbeOnboardingWizardService.uploadImage(formData,fileName)
.then(function (data) {
if(data.type == "success")
console.log(data.fileStorePath);
},function (error) {
console.log(error);
});
};
Error in Jboss:
02:06:25,442 WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (http--0.0.0.0-8080-4) Handler execution resulted in exception: Content type 'null' not supported
If I update the Content-Type to "multipart/form-data" in angular service
Error in Jboss:
Servlet.service() for servlet appServlet threw exception: org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
So, I am trying to generate PDF file by passing the HTML string to the server to generate the byte streams using NReco library http://www.nrecosite.com/pdf_generator_net.aspx and return it back to the client side however, after I converted to blob format and save it with FileSaver library https://github.com/eligrey/FileSaver.js/, the saved file is unable to open.
Below are my code by far:
controller
string HtmlContent = model.HtmlContent;
string PageType = gradeReportPdfBindingModel.PageType;
string FileName = gradeReportPdfBindingModel.FileName;
var pdfDoc = new HtmlToPdfConverter();
if (string.IsNullOrEmpty(PageType))
pdfDoc.Orientation = PageOrientation.Default;
else
{
if (PageType == "Landscape")
pdfDoc.Orientation = PageOrientation.Landscape;
else
pdfDoc.Orientation = PageOrientation.Portrait;
}
var pdfBytes = pdfDoc.GeneratePdf(HtmlContent);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(pdfBytes);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = FileName;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;
Customer.js
Customer.Export(exportModel).then(function (response) {
var file = new Blob([response.data], { type: 'application/pdf' });
saveAs(file, fileName);
});
HTTP POST call
Export: function (model) {
return $http({
method: 'POST',
headers: {
accept: 'application/pdf'
},
url: appSettings.serverPath + '/customer/export',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: $.param(model),
});
},
Response log from browser console:
Object { data: "%PDF-1.4 1 0 obj << /Title (��) /Cr…", status: 200, headers: ed/<(), config: Object, statusText: "OK" }
Edit 1:
I tried to return the result as HttpResponseMessage. How can I consume it on javascript side?
Edit 2:
I figured it out. This is how I do it.
Controller:
[HttpPost]
[Route("export")]
public IHttpActionResult GeneratePdf(model)
{
var pdfBytes = pdfDoc.GeneratePdf(HtmlContent);
var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(300), Priority = CacheItemPriority.NotRemovable };
var cacheId = Guid.NewGuid().ToString();
MemoryCache.Default.Add("pdfBytes_" + cacheId, pdfBytes, policy);
return Ok(cacheId);
}
[Authorize]
[HttpGet]
[Route("getPdf/{cacheId}/{fileName}")]
public HttpResponseMessage DownloadPdf(Guid CacheId, string FileName)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
try
{
var pdfBytes = (Byte[])MemoryCache.Default.Get("pdfBytes_" + CacheId);
MemoryCache.Default.Remove("pdfBytes_" + CacheId);
using (MemoryStream memorystream = new MemoryStream(pdfBytes))
{
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new ByteArrayContent(memorystream.ToArray());
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = FileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
}
customer.js:
Customer.Export(model).then(function (response) {
Customer.DownloadPdf(response.data, fileName).then(function (response) {
var file = new Blob([response.data], { type: "application/pdf" });
saveAs(file, fileName);
}).catch(function (response) {
console.error('Error', response.status, response.data);
});
});
Http calls:
Export: function (model) {
return $http({
method: 'POST',
url: appSettings.serverPath + '/customer/export',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: $.param(model),
});
},
DownloadPdf: function (cacheId, fileName) {
return $http({
method: 'GET',
headers: {
accept: 'application/pdf'
},
responseType: 'arraybuffer',
url: appSettings.serverPath + '/customer/downloadPdf/' + cacheId + '/' + fileName,
headers: { 'Content-Type': 'application/pdf' }
});
}
I basically tested it on my local dev machine and it works, but when I published it Azure Web App, I was unable to get through the POST call and it returns status 500 after some time. Not sure why, but I am suspecting the MemoryCache I am using. Any idea will be appreaciated.
Thanks.
I have an server API and a javascript frontend that downloads files from the server. Im using FileSaver (https://github.com/eligrey/FileSaver.js) to save the downloaded document data to the local filesystem. The solution works well for text files but not for the binaries. I've verified that in both cases the server sends the correct content. It's FileSaver that seems unable to save my binary content correctly. What gets saved is more or less trash, with the wrong file size.
Javascript:
$http({
method: 'GET',
cache: false,
url: <URL>,
headers: {
'Content-Type': 'application/octet-stream',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'accept-encoding': 'identity',
}
}).success(function (data, status, headers, config) {
console.log(headers());
console.log(headers('Content-Disposition'));
var res = headers('Content-Disposition').split('=');
console.log(res);
var fileName = res[1];
console.log(fileName);
var blob = new Blob([data]);
saveAs(blob, fileName);
}
The server side:
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadDocument(#PathParam("documentId") UUID theid) {
doc = <retrieve document>
StreamingOutput stream = new StreamingOutput() {
public void write(OutputStream output) throws IOException, WebApplicationException {
try {
InputStream istream = new ByteArrayInputStream(doc.getData());
System.out.println(doc.getData().length);
IOUtils.copy(istream, output);
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
};
return Response.ok(stream).header("Content-Disposition", "attachment; filename=" + doc.getName()).build();
}