I am getting error while downloading and opening pdf files as
Failed to load PDF document
. But when i tried to download txt file it gets download success fully. I need to this ajax request to be as POST method, So after searching in internet i found this code.
$.ajax({
type: "POST",
url: url,
cache: false,
contentType: false,
processData: false,
success: function (data) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = textName;
link.click();
}, error: function (data) { alert("error"); }});
I use web api for downloading the file
public HttpResponseMessage Download(string fileName)
{
string filePath = Path.Combine(PATH, fileName.Replace('/', '\\'));
byte[] pdf = System.IO.File.ReadAllBytes(filePath);
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(pdf);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "MyPdf.pdf";
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;
}
Please help me
Referencing Download pdf file using jquery ajax
jQuery has some issues loading binary data using AJAX requests, as it
does not yet implement some HTML5 XHR v2 capabilities
It provides two options for you try. I won't repeat them here as they belong to that answer. Check the link.
On the server side, I've had success downloading PDF files using this method.
[HttpPost]
public HttpResponseMessage Download(string fileName)
{
string filePath = Path.Combine(PATH, fileName.Replace('/', '\\'));
byte[] pdf = System.IO.File.ReadAllBytes(filePath);
//content length for header
var contentLength = pdf.Length;
var statuscode = HttpStatusCode.OK;
var result = Request.CreateResponse(statuscode);
result.Content = new StreamContent(new MemoryStream(buffer));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentLength = contentLength;
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "MyPdf.pdf";
return result;
}
Not that much different to your original.
You should also check to make sure that the original file was not corrupt on the server if you are reading from disk/database or if you are dynamically generating the file.
The problem is probably your Content-Disposition header. You need inline, but you have attachment.
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline");
What you have now tells the browser to specifically send the file directly, and never attempt to display it in-browser. Chrome's internal PDF viewer currently chokes when you do this.
More: Content-Disposition:What are the differences between "inline" and "attachment"?
Related
This piece of code will put all the data(string) as an XML format in the Postman. But when I try to save the response in the Postman so, it will save as response.xml, which I don't want. I want that Postman will save my XML content in the form of filename.xml.
So, how should I proceed with? I have to google it and found that something like attachment, content-type, and all must be used. But, didn't solve my case. Please help with your suggestion.
For simplicity, I have reduced the code. Here, graphics is string datatype.
public ActionResult GetXML()
{
string filename = "Demo";
return Content(graphics, "application/xml");
}
You need to set Content-Disposition header so the that browser prompts for saving response.
You can do that by using following approach.
public IActionResult Getxml()
{
var xmlData =
"<records><record><Name>Camacho, Sydnee Q.</Name><Id>1</Id><Age>19</Age><City>Podolsk</City></record><record><Name>Bowman, Lester V.</Name><Id>2</Id><Age>21</Age><City>Padang</City></record></records>";
//sampleFile.xml can be replaced by any filename of your choice.
var fileName = "sampleFile.xml";
Response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}");
return Content(xmlData, "application/xml", Encoding.UTF8);
}
With this change when this URL is browsed in the browser it will prompt save dialog with sampleFile.xml populated as filename.
Also when you save the response from postman it will show sampleFile.xml populated as file name in the dialog.
I hope this will help you solve your issue.
I would suggest to use the FileContentResult for a file download.
public ActionResult GetXML()
{
var fileName = "Demo.xml";
var xml = "...";
return new FileContentResult(Encoding.UTF8.GetBytes(xml), "application/xml; charset=utf-8")
{
FileDownloadName = fileName,
};
}
You need to set the response headers so that the download client automatically treats the content as a "file", the key is the ContentDisposition header, this is how we pass the filename back.
public HttpResponseMessage GetXMLFile()
{
string filename = "Demo";
var byteArray = System.Text.Encoding.UTF8.GetBytes(graphics);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(new MemoryStream(byteArray), byteArray.Length)
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") //attachment will force download
{
FileName = filename
};
return result;
}
I have a web service (.asmx file extension), which creates a Document (Aspose.Words) which I save to MemoryStream.
I would like the user to download the file on click - without having to save the actual file anywhere but to the users computer when he downloads.
I am struggling with this for quite a while...
even if i manage to download the file - the content is not correct.
This is my angular code:
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
Generate() {
this.http.get(
`${this.environment.Url}Generate`, {
headers: headers,
responseType: 'arraybuffer'
}
).subscribe((res: any) => {
this.downLoadFile(res, 'application/msword');
}
);
}
downLoadFile(data: any, type: string) {
let blob = new Blob([data], { type: type});
let url = window.URL.createObjectURL(blob);
let pwa = window.open(url);
if (!pwa || pwa.closed || typeof pwa.closed === 'undefined') {
alert( 'Please disable your Pop-up blocker and try again.');
}
}
And this is what i did in the web service:
public async Task<Stream> Generate() {
Document doc = _Generate(profileToCV);
MemoryStream dstStream = new MemoryStream();
doc.Save(dstStream, SaveFormat.Docx);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(dstStream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "foo";
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/msword");
response.Content.Headers.ContentLength = dstStream.GetBuffer().Length;
return await response.Content.ReadAsStreamAsync();
}
I tried alot of different variations.
like changing the responseType returned to 'blob' or 'blob' as 'json',
or returning the HttpResponseMessage.
nothing worked as expected.
I can't seem to extract the content saved in what is returned to client properly in file.
so - Help! ... Thanks in advance!
Just create a link element and fire click event,
this.blob = new Blob([data], {type: 'application/pdf'}); // your file format
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement('a');
link.href = downloadURL;
link.download = "help.pdf";
link.click();
I have a web api method that returns an HttpResponseMessage containing a PDF file. The method looks something like this:
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream(path, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
When I call this api from client (which is written in angularJS), the Internet Download Manager automatically catches the PDF file and wants to download it. And because I have a security plan for my project, the IDM automatically requests username and password.
Does anyone have an idea about how I'm supposed to programmatically stop IDM from catching the PDF file?
Update: Here's my angularJS code:
$http.post(url, { transactionId: txId }
, {responseType: 'arraybuffer'})
.success(function (response) {
var reader = new FileReader();
var file = new Blob([response.data], {type: 'application/pdf'});
reader.onload = function (e) {
var printElem = angular.element('#printPdfLink');
printElem.attr('target', '_blank');
printElem.attr('href', reader.result);
printElem.attr('ng-click', '');
};
reader.readAsDataURL(file);
})
.error(function (error) {});
Change the mime type to application/octet-stream as a way to work around your problem. Make sure that the file name includes a proper file extension so that it can be recognized by the client system once downloaded.
Another issue is the attachment disposition of the content which typically forces it to save it as a file download. Change it to inline so that the client can consume it without IDM trying to download it as an attachment.
FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
StreamContent content new StreamContent(stream);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline");
content.Headers.ContentDisposition.FileName = fileName;
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = content;
return response;
I have try to use HttpResponseMessage.
If I use ContentDisposition is inline then response break the file. If use attachment then IDM can detect it.
At the end of the day, I found Accept-Ranges header can make download without IDM but it not valid in HttpResponseMessage.
You can try out my code below to make download file without IDM:
[HttpGet]
[Route("~/download/{filename}")]
public void Download(string filename)
{
// TODO lookup file path by {filename}
// If you want to have "." in {filename} you need enable in webconfig
string filePath = "<path>"; // your file path here
byte[] fileBytes = File.ReadAllBytes(filePath);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("Accept-Ranges", "bytes");
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AddHeader("ContentDisposition", "attachment, filename=" + filename);
HttpContext.Current.Response.BinaryWrite(fileBytes);
HttpContext.Current.Response.End();
}
Note: filename parameter serve for download file name so you can config in webconfig if you want to have file extension (disabled by default).
I am trying to download a xlsx extension file to be encaspulated as a httpresponsemessage. The file doesn't appear for downloading, however the XHR Requests in chrome seems to contain the data.
public HttpResponseMessage GetExcelFile(string csvdata)
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
MemoryStream memStream = new MemoryStream(encoding.GetBytes(csvdata));
result.Content = new ByteArrayContent(encoding.GetBytes(csvdata));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "Data.xlsx";
return result;
}
This is the api call that throws the httpresponsemessage into angularjs UI.
Any suggestions to get this to work?.
Hoping that you are using web API get method to return HttpResponseMessage in ValuesController.cs class. ( // GET: api/Values)
assume that ng-click fires at your button click,
add ng-click="TakeIT()" as attribute in button element.
in your angular controller, bind the function to $scope as,
$scope.TakeIT= function () {
window.location = 'api/Values';
};
I need upload csv file and parse it.
I can see file in http body request, but when I pass it in the csvreader I can see base64 string like headers:
On client side I use angularjs:
'uploadBulkUsersFile': {
method: 'POST', url: CONFIG.apiServiceBaseUri + "api/users/bulkUsers",
headers: {
"Content-Type": undefined
},
transformRequest: angular.identity,
withCredentials: true
},
and call:
var _uploadBulkUsersFile = function (bulkUsersFile) {
var fd = new FormData();
fd.append("file", bulkUsersFile);
return usersResource.uploadBulkUsersFile({}, fd, function (result) {
return result;
}).$promise;
};
and on server side I use webapi2:
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var buffer = await file.ReadAsStreamAsync();
TextReader textReader = new StreamReader(buffer);
var csv = new CsvReader(textReader);
var records = csv.GetRecords<BulkUploadUser>().ToList();
return Created("DefaultApi", records);
}
http request payload
------WebKitFormBoundarySKPlgJRINOMnpxVP
Content-Disposition: form-data; name="file"
data:application/vnd.ms-excel;base64,RW1haWwsRmlyc3ROYW1lLExhc3ROYW1lDQpwcm9zdG8uZHVkYUBnbWFpbC5jb20yLERlbmlzLER1ZGFpZXYNCnByb3N0by5kdWRhQGdtYWlsLmNvbSxEZW5pcyxEdWRhaWV2DQpwcm9zdG8uZHVkYUBnbWFpbC5jb20yLERlbmlzLER1ZGFpZXYNCg==
------WebKitFormBoundarySKPlgJRINOMnpxVP--
UPDATE
#Ubercode suggest me convert base 64 to string, I made it, but is looks very disgusting:
var buffer = await file.ReadAsStreamAsync();
TextReader textReader = new StreamReader(buffer);
var text = textReader.ReadToEnd();
var indexOfWord = "base64,";
var base64EncodedBytes = System.Convert.FromBase64String(text.Substring(text.IndexOf(indexOfWord) + indexOfWord.Length));
var encoded = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
TextReader textReader2 = new StringReader(encoded);
var csv = new CsvReader(textReader2);
var records = csv.GetRecords<BulkUploadUser>().ToList();
You need to decode the base64 encoded stuff into your file:
How do I encode and decode a base 64 string
You can tidy your code up a little bit thus:
string text;
using(TextReader textReader = new StreamReader(buffer))
text = textReader.ReadToEnd();
CsvReader csv;
using(var ms
= new MemoryStream(Convert.FromBase64String(text.Substring(text.IndexOf(',') + 1)))
using (var textReader2 = new StreamReader(ms))
csv = new CsvReader(textReader2);
var records = csv.GetRecords<BulkUploadUser>().ToList();
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
HttpPostedFile uploadedFile = HttpContext.Current.Request.Files[0];
var csv = new CsvReader(uploadedFile.InputStream);
The uploaded data is indeed csv, not excel:
Email,FirstName,LastName
prosto.duda#gmail.com2,Denis,Dudaiev
prosto.duda#gmail.com,Denis,Dudaiev
prosto.duda#gmail.com2,Denis,Dudaiev
It would be best to prevent the data being converted to base64 in the first place (it's bigger and needs decoding).
For some reason, the data is send as 'excel' and in base64:
data:application/vnd.ms-excel;base64,
So you probably want to check the following:
headers: {
"Content-Type": undefined
},
transformRequest: angular.identity,
I don't know AngularJS, but you might want to change Content-Type to text/csv and check transformRequest.
Or maybe it gets converted to base64 here:
var fd = new FormData();
fd.append("file", bulkUsersFile);
So it would be best to check where the data gets converted and fix it to send plain text instead. This will prevent the encoding/decoding step, and the data will be smaller too.
Just had the same problem, thanks for the advices in this post.
First of all, there are two possibilities to upload a file: Http-POST-Content or form-data (is post too, but not equally the same).
I'm using the simpler method, the Http-POST-Content. There are no problems with base64 encoding, etc, the solution stays simple, but I think it can be pretty uneficcent with huge files.
I used the follwing code in the angular Project:
Angular Code
Import Dialog (html):
<input hidden #imageInput type="file" accept=".csv" (change)="processFile(imageInput)">
<button mat-button class="image-upload-button full-width" (click)="imageInput.click()">
<mat-icon>add</mat-icon>
</button>
Dialog Code (.ts)
const file: File = imageInput.files[0];
this.dialogRef.close(file);
ts-Code executed after dialog close
const url = 'myapi/Import';
return this.http.post<ImportResult>(url, file).toPromise();
C# Code
try
{
var result = new ImportResult();
s = System.Web.HttpContext.Current.Request.GetBufferedInputStream();
using (var sr = new System.IO.StreamReader(s))
{
s = null;
var reader = new CsvReader(sr, new CsvConfiguration
{
Delimiter = ";"
});
var records = reader.GetRecords<RowEntry>();
...
}
return result;
}
finally
{
s?.Dispose();
}
Hope this will help, it's a simple solution working fine.