I am using WebAPI for downloading a .pdf file like this:
[HttpGet]
public async Task<HttpResponseMessage> DownloadFile(string id, bool attachment = true)
{
HttpResponseMessage result = null;
try
{
MyService service = new MyService();
var bytes = await service.DownloadFileAsync(id);
if (bytes != null)
{
result = GetBinaryFile(personalDocument, string.Format("{0}.pdf", id), attachment);
}
else
{
result = new HttpResponseMessage(HttpStatusCode.NotFound);
}
}
catch (Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "ServerError" });
}
return result;
}
private HttpResponseMessage GetBinaryFile(byte[] bytes, string fileName, bool attachment)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
// result.Content = new ByteArrayContent(bytes);
result.Content = new StreamContent(new System.IO.MemoryStream(bytes));
//result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline");
if (attachment)
{
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
}
result.Content.Headers.ContentDisposition.FileName = fileName;
result.Content.Headers.ContentLength = bytes.Length;
return result;
}
The issue is: when I download file for the first time it freezes the browser, and cannot perform anything. But after a refresh of the page, every time the file download is successful.
I tried to use ByteArrayContent or StreamContent but same thing happens. I also tried to use MediaTypeHeaderValue("application/octet-stream") or MediaTypeHeaderValue("application/pdf"), but again - the same issue occurred.
So I find that the problem is
if (attachment)
{
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
}
If I set attachment to be true it set ContentDisposition to be ContentDispositionHeaderValue("attachment") (it is for download only)
If I set attachment to be false it open the file immediately and doesn't freeze the browser..
Any idea? Thanks!
Related
I am trying to open a new tab and display a downloaded pdf from Core 2.2 web API in angular 9
public GeneratePdf(id: string): Observable<Blob> {
return this.http.get( this.urlPdf + '?id=' + id, { responseType: 'blob' });
}
this.dataProvider.GeneratePdf(id).subscribe({
next: (blob) => {
const blobpart = new Blob([blob], { type: 'application/pdf' });
var fileUrl = URL.createObjectURL(blobpart);
let win: any = this.getWindow();
win.open(fileUrl);
},
error: (err) => this.error.handelHttpError(err),
});
API
[HttpGet]
[Route("PDF")]
public async Task<HttpResponseMessage> PDF(Guid Id) {
_logger.LogInformation("Request:" + Request.GetDisplayUrl());
var endpoint = _appSettings.PdfEndpoint;
try {
var param = await _dal.GetPDFParameters(Id, endpoint);
// Get PDF stream
HttpResponseMessage response = await client.GetAsync(param.EndpontUrl);
if(response.IsSuccessStatusCode) {
using(HttpContent content = response.Content) {
var memStream = new MemoryStream();
Stream sourceStream = await content.ReadAsStreamAsync();
sourceStream.CopyTo(memStream);
var result = new HttpResponseMessage(HttpStatusCode.OK) {
Content = new ByteArrayContent(memStream.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") {
FileName = Id.ToString() + ".pdf"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
} else {
var result = new HttpResponseMessage(HttpStatusCode.BadRequest);
return result;
}
} catch(Exception ex) {
_logger.LogError(ex, "Exception error");
var result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
return result;
}
}
it doesn't throw an error, it just opens a json object in the new tab, and the size of the object looks too small as the pdf content is over 3k bytes
{"version":{"major":1,"minor":1,"build":-1,"revision":-1,"majorRevision":-1,"minorRevision":-1},"content":{"headers":[{"key":"Content-Disposition","value":["attachment; filename=bd94ee98-65be-4c4f-a001-abecaf1a0644.pdf"]},{"key":"Content-Type","value":["application/octet-stream"]}]},"statusCode":200,"reasonPhrase":"OK","headers":[],"requestMessage":null,"isSuccessStatusCode":true}
update, there was a small error on the Blob, I was not passing in the blobpart to the url.CreateObjecturl Now the app loads the new tab, but states an invalid pdf as there is no content. I know the pdf bytes are going into the content of the api result as i have checked it. and converted it to a string to ensure it's a pdf, which it is.
Thanks for taking the time too look.
I have a Web Api controller, that gets file. (Server)
[HttpGet]
[Route("api/FileDownloading/download")]
public HttpResponseMessage GetDocuments()
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
var fileName = "QRimage2.jpg";
var filePath = HttpContext.Current.Server.MapPath("");
var fileBytes = File.ReadAllBytes(#"c:\\TMP\\QRimage2.jpg");
MemoryStream fileMemStream = new MemoryStream(fileBytes);
result.Content = new StreamContent(fileMemStream);
var headers = result.Content.Headers;
headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
headers.ContentDisposition.FileName = fileName;
headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
headers.ContentLength = fileMemStream.Length;
return result;
}
And Xamarin Android client, that downloading the file using the controller (http://localhost:6100/api/FileDownloading/download)
public void DownloadFile(string url, string folder)
{
string pathToNewFolder = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, folder);
Directory.CreateDirectory(pathToNewFolder);
try
{
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
string pathToNewFile = Path.Combine(pathToNewFolder, Path.GetFileName(url));
webClient.DownloadFileAsync(new Uri(url), null);
}
catch (Exception ex)
{
if (OnFileDownloaded != null)
OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
}
}
Everithing works fine, but on my Android device in file explorer i have file with "download" file name instead of "QRimage2.jpg". How can I get actual file name using this controller?
You will need use the web response to read the content disposition. So, we can't use DownloadFileAsync directly.
public async Task<string> DownloadFileAsync(string url, string folder)
{
var request = WebRequest.Create(url);
var response = await request.GetResponseAsync().ConfigureAwait(false);
var fileName = string.Empty;
if (response.Headers["Content-Disposition"] != null)
{
var contentDisposition = new System.Net.Mime.ContentDisposition(response.Headers["Content-Disposition"]);
if (contentDisposition.DispositionType == "attachment")
{
fileName = contentDisposition.FileName;
}
}
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException("Cannot be null or empty.", nameof(fileName));
}
var filePath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, folder, fileName);
using (var contentStream = response.GetResponseStream())
{
using (var fileStream = File.Create(filePath))
{
await contentStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
return filePath;
}
Is this always going to be a jpg? If so I'd change the MediaTypeHeaderValue to image/jpeg - By doing that you are telling the browser the exact type of file, instead of a generic file. I'm thinking this is the issue since you are telling the Android Browser it's just a generic binary file.
Do I need Content-Type: application/octet-stream for file download?
i am trying to download a file (.docx) from asp.net web api.
Since i already have a document in the server i set the path to existing one and then i follow something sugested on stackoverflow and do this:
docDestination is my path.
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(docDestination, FileMode.Open, FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
return result;
after that on my client side i try to do this:
.then(response => {
console.log("here lives the response:", response);
var headers = response.headers;
var blob = new Blob([response.body], { type: headers['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "Filename";
link.click();
}
this is what i get on my response
what i get:
any help?
Just add ContentDisposition to your response header with value of attachment and the browser will interpret it as a file that needs to be download
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(docDestination, FileMode.Open,FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "document.docx"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
return result;
Take a look in this link for more information in ContentDisposition header
Change return type of your method. You can write method something like this.
public FileResult TestDownload()
{
FileContentResult result = new FileContentResult(System.IO.File.ReadAllBytes("YOUR PATH TO DOC"), "application/msword")
{
FileDownloadName = "myFile.docx"
};
return result;
}
In client side, you just need to have a link button. Once you click on the button, file will be downloaded. Just write this line in cshtml file. replace controller name with your controller name.
#Html.ActionLink("Button 1", "TestDownload", "YourCOntroller")
When you have a stream open, you want to return it's content as a file
[HttpGet]
public async Task<FileStreamResult> Stream()
{
var stream = new MemoryStream(System.IO.File.ReadAllBytes("physical path of file"));
var response = File(stream, "Mime Type of file");
return response;
}
You use it when you have a byte array you would like to return as a file
[HttpGet]
public async Task<FileContentResult> Content()
{
var result = new FileContentResult(System.IO.File.ReadAllBytes("physical path of file"), "Mime Type of file")
{
FileDownloadName = "Your FileName"
};
return result;
}
when you have a file on disk and would like to return it's content (you give a path)-------------only in asp.net core
[HttpGet]
public async Task<IActionResult> PhysicalPath()
{
var result = new PhysicalFileResult("physical path of file", "Mime Type of file")
{
FileDownloadName = "Your FileName",
FileName = "physical path of file"
};
return result;
}
Here's what I have tried so far:
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
try
{
StringBuilder strBuilder = GenerateExcelFormat(modelPaymentDetails);
//byte[] array = Encoding.ASCII.GetBytes(strBuilder.ToString());
response.Content = new StringContent(strBuilder.ToString());
//response.Content = new ByteArrayContent(array);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/ms-excel");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); //attachment will force download
response.Content.Headers.ContentDisposition.FileName = "PaymentSummary.xls";
return response;
}
catch (Exception ex)
{
return response;
}
but didn't download an excel file. Where I'm going wrong?
I'm attempting to return an image from a server route, but I'm getting one that is 0 bytes. I suspect it has something to do with how I'm using the MemoryStream. Here's my code:
[HttpGet]
[Route("edit")]
public async Task<HttpResponseMessage> Edit(int pdfFileId)
{
var pdf = await PdfFileModel.PdfDbOps.QueryAsync((p => p.Id == pdfFileId));
IEnumerable<Image> pdfPagesAsImages = PdfOperations.PdfToImages(pdf.Data, 500);
MemoryStream imageMemoryStream = new MemoryStream();
pdfPagesAsImages.First().Save(imageMemoryStream, ImageFormat.Png);
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StreamContent(imageMemoryStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = pdf.Filename,
DispositionType = "attachment"
};
return response;
}
Through debugging I have verified that the PdfToImages method is working and that imageMemoryStream gets filled with data from the line
pdfPagesAsImages.First().Save(imageMemoryStream, ImageFormat.Png);
However in running it, I receive an attachment that is properly named but is 0 bytes. What do I need to change in order to receive the whole file? I think it's something simple but I'm not sure what. Thanks in advance.
After writing to the MemoryStream, Flush it then set Position to 0:
imageMemoryStream.Flush();
imageMemoryStream.Position = 0;
You should rewind MemoryStream to beginning before passing it to response. But you'd better use PushStreamContent:
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new PushStreamContent(async (stream, content, context) =>
{
var pdf = await PdfFileModel.PdfDbOps.QueryAsync(p => p.Id == pdfFileId);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = pdf.Filename,
DispositionType = "attachment"
};
PdfOperations.PdfToImages(pdf.Data, 500).First().Save(stream, ImageFormat.Png);
}, "image/png");
return response;