I have a page that allows a user to click a button, and it makes a WebAPI call to my .Net backend controller which generates a PDFSharp document object. I need to return that (binary?) object to the Knockout/Javascript caller on the UI, and present a Save As dialog... or else just start downloading it automatically.
This is my attempt, but it's not right.
[System.Web.Http.Route("GeneratePdf"), System.Web.Http.HttpPost]
public HttpResponseMessage GeneratePdf(PlateTemplateExtendedDto data)
{
var doc = GeneratePdf(new PlateService().CreateTemplate(true), "my.pdf");
HttpResponseMessage result = null;
var pdfContent = new MemoryStream();
doc.Save(pdfContent);
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(new FileStream(pdfContent, FileMode.Open, FileAccess.Read));
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "SampleImg"
//pdfContent.Position = 0;
var res = new HttpResponseMessage();
res.Content = new ByteArrayContent(pdfContent.ToArray());
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
res.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
res.Content.Headers.ContentDisposition.FileName = data.Description;
res.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return res;
}
Firstly, the line:
new StreamContent(new FileStream(pdfContent, FileMode.Open, FileAccess.Read));
is invalid. 'pdfContent' should be a string, I think.
Secondly, I'm not sure how to handle a 'HttpResponseMessage' on the front end. How do I turn that into a Download?
Web api may be something like this..
[System.Web.Http.Route("GeneratePdf"), System.Web.Http.HttpGet]
public IHttpActionResult GeneratePdf(PlateTemplateExtendedDto data)
{
var doc = GeneratePdf(new PlateService().CreateTemplate(true), "my.pdf");
var pdfContent = new MemoryStream();
doc.Save(pdfContent);
var content = new StreamContent(pdfContent);
content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
content.Headers.ContentDisposition.FileName = data.Description;
content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Headers.ContentLength = pdfContent.GetBuffer().Length;
return Ok(content);
}
you only set api url with PlateTemplateExtendedDto parameters to window.location on client side
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 want to send an image as json to a post api,but when i send the url of the image in json the reponse shows error.But in postman when i send the image with file upload it works.
My json:
{
name:"xxx",
image:"someurl"
}
my c# code:
{
string url = "xxxxxxxxxx";
HttpWebRequest webRequest = null;
HttpWebResponse webResponse = null;
string responseFromServer = "";
webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.ContentType = "application/json";
webRequest.Method = "POST";
webRequest.ReadWriteTimeout = 300000;
// Get the response.
using (var streamWriter = new StreamWriter(webRequest.GetRequestStream()))
{
streamWriter.Write(jsonREQ);
streamWriter.Flush();
streamWriter.Close();
}
using (webResponse = (HttpWebResponse)webRequest.GetResponse())
{
Stream responseStream = webResponse.GetResponseStream();
using (StreamReader sr = new StreamReader(responseStream))
{
responseFromServer = sr.ReadToEnd();
sr.Close();
}
webResponse.Close();
}
return responseFromServer;
}
Here the json request have the json,when i use this i dont get the expected response,but when i use postman with file upload,
It works ,Kindly suggest me a way to send my image in json.(* have tried base64 and byte,it doesnt work as expected*)
Try this:
var formData = new FormData();
formData.append("image", fileInputElement.files[0]);
var request = new XMLHttpRequest();
request.open("POST", "http://example.com/submitform.php");
request.send(formData);
And this on the server to upload to another server:
string responseString;
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var formData = new MultipartFormDataContent {
{ new StreamContent(fileStream), #"image", Path.GetFileName(filePath) }
};
var request = (HttpWebRequest)WebRequest.Create("URL");
request.Method = WebRequestMethods.Http.Post;
request.AllowWriteStreamBuffering = false;
request.SendChunked = true; // set to true to avoid exception when AllowWriteStreamBuffering = false and ContentLength is not set
request.Headers.Add("abc", "def");
request.ContentType = formData.Headers.ContentType.ToString();
try
{
// Get the request stream with the default timeout
using (Stream requestStream = await request.GetRequestStreamAsyncWithTimeout())
{
// Upload the file with no timeout
await formData.CopyToAsync(requestStream);
}
// Get response with the default timeout, and parse the response body
using (WebResponse response = await request.GetResponseAsyncWithTimeout())
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
responseString = reader.ReadToEnd();
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
{
throw new TimeoutException("Timeout while uploading file.", ex);
}
throw;
}
}
ResultModel result = JsonConvert.DeserializeObject<ResultModel>(responseString);
These extensions so your upload request won't be timeout if you have a big file or slow connection:
public static class WebRequestExtensions
{
public static async Task<Stream> GetRequestStreamAsyncWithTimeout(this WebRequest request, int? millisecondsTimeout = null)
{
Task<Stream> getTask = request.GetRequestStreamAsync();
if (await Task.WhenAny(getTask, Task.Delay(request.Timeout)) == getTask)
{
// task completed without timeout
return getTask.Result;
}
else
{
// timeout
var ex = new TimeoutException();
throw new WebException(ex.Message, ex, WebExceptionStatus.Timeout, null);
}
}
public static async Task<WebResponse> GetResponseAsyncWithTimeout(this WebRequest request, int? millisecondsTimeout = null)
{
Task<WebResponse> getTask = request.GetResponseAsync();
if (await Task.WhenAny(getTask, Task.Delay(request.Timeout)) == getTask)
{
// task completed without timeout
return getTask.Result;
}
else
{
// timeout
var ex = new TimeoutException();
throw new WebException(ex.Message, ex, WebExceptionStatus.Timeout, null);
}
}
private static T AsyncToSyncWithTimeout<T>(
Func<AsyncCallback, object, IAsyncResult> begin,
Func<IAsyncResult, T> end,
int millisecondsTimeout)
{
var iar = begin(null, null);
if (!iar.AsyncWaitHandle.WaitOne(millisecondsTimeout))
{
var ex = new TimeoutException();
throw new WebException(ex.Message, ex, WebExceptionStatus.Timeout, null);
}
return end(iar);
}
}
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!
I can't get it to work to download an excel file that was created by closedxml through web API.
If I save the file on the server it looks good, but as soon as I put it in a stream and return it to the web api, then only a corrupt file is recieved in the browser.
As suggested on several posts I use httpResponseMessage, but also in the browser the filename in the header never arrives.
We are using:
"Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net461
"ClosedXML" version="0.88.0" targetFramework="net461"
WebAPI Code:
var wb = new XLWorkbook();
var ws = wb.Worksheets.Add("Parcel List");
MemoryStream fs = new MemoryStream();
wb.SaveAs(fs);
fs.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(fs.GetBuffer());
result.Content.Headers.ContentLength = fs.Length;
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "List" + "_" + DateTime.Now.ToShortDateString() + ".xlsx"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
Here the javascript code:
context.$http.post(config.get_API_URL() + 'api_call', excel_list,
{responseType: 'application/octet-stream'})
.then(
success_function,
error_function)
}
success_function:
function(response) {
var headers = response.headers;
var blob = new Blob([response.body],
{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'},
);
window.open(window.URL.createObjectURL(blob));
}
I could successfully download a workbook with this code now:
using ClosedXML.Excel;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace ClosedXML.Extensions.WebApi.Controllers
{
public class ValuesController : ApiController
{
public IHttpActionResult Get(int id)
{
return new TestFileActionResult(id);
}
}
public class TestFileActionResult : IHttpActionResult
{
public TestFileActionResult(int fileId)
{
this.FileId = fileId;
}
public int FileId { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
var ms = new MemoryStream();
using (var wb = new XLWorkbook())
{
var ws = wb.AddWorksheet("Sheet1");
ws.FirstCell().Value = this.FileId;
wb.SaveAs(ms);
ms.Seek(0, SeekOrigin.Begin);
response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(ms);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "test.xlsx";
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.Content.Headers.ContentLength = ms.Length;
ms.Seek(0, SeekOrigin.Begin);
}
return Task.FromResult(response);
}
}
}
Have a look at the Mvc extension package at https://www.nuget.org/packages/ClosedXML.Extensions.Mvc/
PS: I've been told I have to disclaim this everytime. I'm the maintainer of ClosedXML and ClosedXML.Extensions.Mvc.
The problem seems to be that the response type for the web api call has to be {responseType: 'arraybuffer'} instead of {responseType: 'application/octet-stream'}
context.$http.post('api-url', excel_list,
{responseType: 'arraybuffer'})
.then(
success_function,
error_function)
}
Thanks anyhow for your quick help
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.