I am trying to download a pdf file from server and show it in browser. The web api method that I have returns the file as httpResponseMessage and that is working fine because it returns the file. But on the AngularJs side I am not able to display the file. Can somebody help me understand what am I missing?
Web Api Method:
public HttpResponseMessage GetHelpReferenceDocs(Guid streamKey)
{
var fakeFileName = GetStream(streamKey); // If this succeeds, stream is known and unexpired.
// Internal file name
string staticFileName = helpFiles[fakeFileName];
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Static/" + staticFileName);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(mappedPath, FileMode.Open, FileAccess.Read, FileShare.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(mappedPath);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentLength = stream.Length;
return result;
}
AngularJS:
function loadDocument(fileName) {
REST.post(commonService.constants.webapi.helpFileStreamKey + fileName)
.then(function(response) {
var streamGuid = response.data;
REST.get(commonService.constants.webapi.helpReferenceGuide + streamGuid).then(function (response) {
$window.open(response.data);
});
})
.catch(function (e) { $scope.errorHandler(moduleName, e); })
.finally($scope.waitOn);
}
Take a look at the suggestions here, or alternatively you can try an npm module specifically for displaying pdfs where this is already taken care of for you, such as this one.
Ok I found the solution. Needed to modify my Angular code a little bit and it worked fine. Here is the code if somebody facing the same problem:
function loadDocument(fileName) {
REST.post(commonService.constants.webapi.helpFileStreamKey + 'firmUserGuid')
.then(function(response) {
var streamGuid = response.data;
REST.get(commonService.constants.webapi.helpReferenceGuide + streamGuid).then(function (response) {
var pdfFileURL = response.config.url;
$window.open(pdfFileURL);
});
})
.catch(function (e) { $scope.errorHandler(moduleName, e); })
.finally($scope.waitOn);
}
Related
I am working on a spring boot web application, where I want to upload multiple images of a product at a time along with other fields (for example product name, SKU code, category, tags, subcategory, etc). I have written code for RESTful API to upload multiple images and it is working perfectly for me. I tested API using postman and it is working fine. But, I don't know how to do it from the front end. I am showing you my front-end code below, where I am sending a single image to my controller using Ajax.
$("#file").change(function(){
var formData = new FormData();
var fileSelect = document.getElementById("file");
if(fileSelect.files && fileSelect.files.length == 1) {
var file = fileSelect.files[0];
formData.set("file",file,file.name);
}else{
$("#file").focus();
return false;
}
var request = new XMLHttpRequest();
try {
request.onreadystatechange=function() {
if(request.readyState==4) {
var v = JSON.parse(request.responseText);
if(v.status==="OK") {
alert("Product Image Uploaded Successfully")
document.getElementById('imagepath').value = v.response;
}
}
}
request.open('POST',"<%=AkApiUrl.testuploadfile%>");
request.send(formData);
} catch(e) {
swal("Unable to connect to server","","error");
}
});
As I told you, the above code is to send a single file at a time. I am showing you my API controller code also:
#RequestMapping(value = AkApiUrl.testuploadfile, method = { RequestMethod.POST, RequestMethod.GET }, produces = {MediaType.APPLICATION_JSON_VALUE }) public ResponseEntity<?> testuploadfile(HttpServletRequest request, #RequestParam("files") MultipartFile[] files) {
CustomResponse = ResponseFactory.getResponse(request);
String imgurl = "NA";
try {
String path = Constants.webmedia;
String relativepath = "public/media/";
System.out.println("Here is the image: ");
List<MultipartFile> multifile = Arrays.asList(files);
if( null != multifile && multifile.size()>0) {
for (int i=0; i < multifile.size(); i++) {
String filename = files[i].getOriginalFilename();
String extension = filename.substring(filename.lastIndexOf("."), filename.length());
int r = (int )(Math.random() * 500 + 1);
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmss");
Date date = new Date();
String formatdate = format.format(date);
formatdate = "ECOM" + formatdate + r;
byte[] bytes = files[i].getBytes();
BufferedOutputStream stream = new BufferedOutputStream( new FileOutputStream(new File(path + File.separator + formatdate + extension)));
stream.write(bytes);
stream.flush();
stream.close();
String newimgurl = relativepath + formatdate + extension;
imgurl = imgurl+"##"+newimgurl;
if(imgurl != null) {
CustomResponse.setResponse(imgurl);
CustomResponse.setStatus(CustomStatus.OK);
CustomResponse.setStatusCode(CustomStatus.OK_CODE);
}
}
}
} catch (Exception e) {
e.printStackTrace();
CustomResponse.setResponse(null);
CustomResponse.setStatus(CustomStatus.Error);
CustomResponse.setStatusCode(CustomStatus.Error_CODE);
CustomResponse.setResponseMessage(CustomStatus.ErrorMsg);
}
return new ResponseEntity<ResponseDao>(CustomResponse, HttpStatus.OK);
}
This API is working fine, I am getting desired response. But I do not know how should I implement this thing on the JSP page. Please, any suggestions would be appreciated.
I am trying to download a file using knockout v3.2.0, webapi, odata and get this error when I try to return the file as HttpResponseMessage.
Here is my controller code:
[EnableQuery]
public HttpResponseMessage GetAttachment([FromODataUri] int key)
{
try
{
DataAccess.Attachment a = db.Attachments.Where(x => x.AttachmentId == key).FirstOrDefault();
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
MemoryStream memStream = new MemoryStream();
memStream.Write(a.AttachmentData, 0, a.AttachmentData.Length);
result.Content = new StreamContent(memStream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition.FileName = a.AttachmentName;
return result;
//return Request.CreateResponse(HttpStatusCode.OK, result);
}
catch (Exception exception)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exception.Message);
return null;
}
}
That's how I am trying to download from JavaScript:
self.downloadDocument = function (attachmentId) {
var serviceRequestUrl = dbhdd.buildUrl.buildSPContextUrl("/api/Attachments(" + 1 + ")");
window.location.href = serviceRequestUrl;
};
Which gives me this error- Queries can not be applied to a response content of type 'System.Net.Http.StreamContent'. The response content must be an ObjectContent.
I am relatively new to this. Any guidance in fixing this/alternate approach will be highly appreciated.
So I removed [EnableQuery] and it worked both in IE and Chrome!
Seems like a straightforward issue, but how can you retrieve an image stored in a database, using Web API, then display it using Angular?
Here is an example Web API service which is correctly returning a JPG file (using HttpResponseMessage):
public HttpResponseMessage GetIncidentImages(Guid IncidentIDX) {
var response = new HttpResponseMessage();
List<T_EM_INCIDENT_ATTACH> att = db_Layer.GetT_EM_INCIDENT_ATTACH_byIncidentIDX(IncidentIDX);
if (att != null)
{
if (att.Count > 0)
{
var pictureBytes = att[0].ATTACH_CONTENT; //ATTACH_CONTENT is a byte array
if (pictureBytes == null)
response.StatusCode = HttpStatusCode.NotFound;
else
{
response.Content = new ByteArrayContent(pictureBytes);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
}
}
else
response.StatusCode = HttpStatusCode.NotFound;
}
return response;
}
Then on the http client-side, I am using angular to retrieve the data. Data is definitely getting retrieved, but just not displayed.
dbManagerService.syncIncidentAttach(ehConstants.incidenT_IDX).then(function (res) {
console.log("return", res);
$scope.cameraPic = res;
});
function _syncIncidentAttach(incIDX) {
var deferred = $q.defer();
$http.get($rootScope.serverBaseUrl + 'api/incident/GetIncidentImages?IncidentIDX=' + incIDX, { responseType: "blob" })
.success(function (res, status, headers, config) {
// encode data to base 64 url
fr = new FileReader();
fr.onload = function () {
// this variable holds your base64 image data URI (string)
// use readAsBinary() or readAsBinaryString() below to obtain other data types
console.log(fr.result);
};
fr.readAsDataURL(res);
deferred.resolve(fr);
})
.error(function(data, status, headers, config) {
conole.log('error getting image');
});
return deferred.promise;
}
And the html:
<img ng-src="{{cameraPic}}" /> </div>
Looking at your server side code, I think you can directly write like this:
<img ng-src="{{serverBaseUrl}}api/incident/GetIncidentImages?IncidentIDX={{ehConstants.incidenT_IDX}}" />
Just make sure you are replacing ehConstants.incidenT_IDX with actual content.
As documented in this answer, you can also do something like
<img ng-src="{{'data:image/png;base64,' + main.user.imageData}}">
I'm using the $http.get(...) method in SPA to get pdf download.
In the SPA ,print method give the blank pdf .
But when i did the debug, data come from the API.
Can you help on this?
This is the API implementation to response the stream out as application/pdf
public Stream GetConsumerInformationReport(Guid id)
{
..........
var stream = cryRpt.ExportToStream(ExportFormatType.PortableDocFormat);
return stream;
}
SPA implementation of get the data from API
var print = function () {
var downloadPath = apiEndPoint + 'Reports/' + $state.current.data.printPrefix + '.pdf';
$http.get(downloadPath,httpConfig).
success(function (data) {
var blob = new Blob([data], { type: "application/pdf" });
var objectUrl = URL.createObjectURL(blob);
$window.open(objectUrl);
}).
error(function (data, status, headers, config) {
// if there's an error you should see it here
});
};
Use FileSaver.js from here http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js
Then define your download method like this. Take it only as an inspiration, do not copy-paste-run :)
The original version can be found here - http://davidjs.com/2015/07/download-files-via-post-request-in-angularjs/
//Define method download() in your ng controller
$scope.download = () => {
//Indicates that download is in progress
$scope.isDownloading = true;
return $http.get(downloadPath,httpConfig).$promise.then((data: any) => {
//using saveAs.js (part of upcoming HTML5 API, but so far a polyfill)
var blob = data.response.blob;
var fileName: string = data.response.fileName || 'document.pdf';
//SaveAs is available at saveAs.js from http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js
(<any>$window).saveAs(blob, fileName);
})
.finally(() => {
$scope.isDownloading = false;
});
}
I am trying to generate a CSV file from my web api and receive that file through angularjs. I have an API controller like below:
[HttpPost]
public HttpResponseMessage GenerateCSV(FieldParameters fieldParams)
{
var output = new byte[] { };
if (fieldParams!= null)
{
using (var stream = new MemoryStream())
{
this.Serialize(fieldParams, stream);
stream.Flush();
output = stream.ToArray();
}
}
var result = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(output) };
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Fields.csv"
};
return result;
}
In my angularjs, i have this:
$scope.save = function () {
var csvInput= extractDetails();
// File is an angular resource. We call its save method here which
// accesses the api above which should return the content of csv
File.save(csvInput, function (content) {
console.log(content);
// only creates a csv file with "[object Object]" written in it
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,\uFEFF' + encodeURI(content.Parameters);
hiddenElement.target = '_blank';
hiddenElement.download = 'myFile.csv';
hiddenElement.click();
});
};
Lets say for example, in my API controller, the content of response is
output
{byte[152]}
[0]: 83
[1]: 101
[2]: 44
[3]: 67
[4]: 10
When I receive this in angularjs and I put the value of content in the console log (chrome), this is what I get:
{Parameters: Array[1], $promise: Object, $resolved: true, $get: function, $save: function…}
0:"S"
1: "e"
2: ","
3: "C"
4: "↵"
$promise: object
$resolved: true`
Why did the content received in the angularjs contain characters
already instead of a byte of array?
How can I control the content in such a way that I will only use
the csv related data and remove $promise and $resolved? Why are they included in the first place? How to remove them?
What is the proper way of generating a csv if what I am doing is
wrong? :|
Forgot to update this, but i now found a way to solve this:
There will be two API's, one (POST) will remember the data to be used in the processing and another one (GET) which will dispense the file.
POST:
[HttpPost]
public async Task<HttpResponseMessage> BuildFile(FileParameters fileParams)
{
var guid = Guid.NewGuid().ToString();
if (fileParams!= null)
{
await Task.Run(() => FileContents.Add(guid, fileParams));
return this.Request.CreateResponse(HttpStatusCode.OK, new { Value = guid });
}
return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid data");
}
In AngularJs, remember the guid returned and pass this to another api:
location.href = '/api/file/generatefile' + '?guid=' + generatedGuidFromAPI + '&reportName=' + $scope.reportName;
And here is the generatefile API controller in MVC:
GET:
[HttpGet]
public async Task<HttpResponseMessage> GenerateFile(string guid, string reportName)
{
byte[] output = null;
if (FileContents.ContainsKey(guid))
{
await Task.Run(() =>
{
using (var stream = new MemoryStream())
{
this.CreateFile(FileContents[guid], stream);
stream.Flush();
output = stream.ToArray();
}
});
}
FileContents.Remove(guid);
if (output != null)
{
var result = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(output) };
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = reportName + ".csv"
};
return result;
}
return this.Request.CreateErrorResponse(HttpStatusCode.NoContent, "No record found");
}
using location.href will cause the browser to automatically download the file, asking you whether to save it or not.
Here's how I do it: (tested in chrome)
// WebAPI controller action
public IHttpActionResult Get(string rpt, DateTime date)
{
List<DailyMIReportViewModel> list = new List<DailyMIReportViewModel>();
// Do some stuff to generate list of items
// Download Requested
if (rpt == "dailymidl")
{
// Create byte array of csv
byte[] csvData = WriteCsvWithHeaderToMemory(list);
// create FileContentResult of cdv byte array
FileContentResult result = new FileContentResult(csvData, "application/octet-stream");
// set filename in FileContentResult
result.FileDownloadName = "Report.csv";
return Ok(result);
}
// Data Requested
return Ok(list);
// Client-side angularjs
// Called on button click
$scope.generateMIDownload = function (forDate) {
// Using $resource to return promise with FileContentResult payload
reportsRepository.dailymidl(forDate).$promise.then(
function (data) {
//ok
// NOTE: the base64 part is what got it working
var dataUrl = 'data:application/octet-stream;base64,' + data.FileContents
var link = document.createElement('a');
angular.element(link)
.attr('href', dataUrl)
.attr('download', data.FileDownloadName)
.attr('target','_blank')
link.click();
},
function (response) {
//not ok
});
}
// Reports Repository (for ref)
angular.module('msgnr').factory('reportsRepository', function ($resource) {
return {
dailymidl: function (date) {
return $resource('/api/Report/', { rpt: 'dailymidl', date: date, toDate: date }).get();
}
}
});
Incase it helps anyone else.