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;
}
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 struggling with being able to create a file with its data based on the byte array returned from the WebAPI. The following is my code for making the call to the web api
using (var http = new WebClient())
{
string url = string.Format("{0}api/FileUpload/FileServe?FileID=" + fileID, webApiUrl);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
http.DownloadDataCompleted += Http_DownloadDataCompleted;
byte[] json = await http.DownloadDataTaskAsync(url);
}
The api code is
[HttpGet]
[Route("FileServe")]
[Authorize(Roles = "Admin,SuperAdmin,Contractor")]
public async Task<HttpResponseMessage> GetFile(int FileID)
{
using (var repo = new MBHDocRepository())
{
var file = await repo.GetSpecificFile(FileID);
if (file == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var stream = File.Open(file.PathLocator, FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
return response;
}
}
I receive a byte array as a response however am unable to create the corresponding file from that byte array. I have no idea how to convert the byte array into the relevant file type (such as jpg, or pdf based on file type in the web api). any help will be appreciated.
Alright so there are a few ways of solving your problem firstly, on the server side of things you can either simply send the content type and leave it at that or you can also send the complete filename which helps you even further.
I have removed the code that is specific to your stuff with basic test code, please just ignore that stuff and use it in terms of your code.
Some design notes here:
[HttpGet]
[Route("FileServe")]
[Authorize(Roles = "Admin,SuperAdmin,Contractor")]
public async Task<HttpResponseMessage> GetFileAsync(int FileID) //<-- If your method returns Task have it be named with Async in it
{
using (var repo = new MBHDocRepository())
{
var file = await repo.GetSpecificFile(FileID);
if (file == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var stream = File.Open(file.PathLocator, FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName=Path.GetFileName(file.PathLocator)};
return response;
}
}
Your client side code has two options here:
static void Main(string[] args)
{
using (var http = new WebClient())
{
string url = string.Format("{0}api/FileUpload/FileServe?FileID={1}",webApiUrl, fileId);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
var response = http.OpenRead(url);
var fs = new FileStream(String.Format(#"C:\Users\Bailey Miller\Downloads\{0}", GetName(http.ResponseHeaders)), FileMode.Create);
response.CopyTo(fs); <-- how to move the stream to the actual file, this is not perfect and there are a lot of better examples
fs.Flush();
fs.Close();
}
}
private static object GetName(WebHeaderCollection responseHeaders)
{
var c_type = responseHeaders.GetValues("Content-Type"); //<-- do a switch on this and return a really weird file name with the correct extension for the mime type.
var cd = responseHeaders.GetValues("Content-Disposition")[0].Replace("\"", ""); <-- this gets the attachment type and filename param, also removes illegal character " from filename if present
return cd.Substring(cd.IndexOf("=")+1); <-- extracts the file name
}
In my asp.net webapi project I need to return string json data as a file. What is also important I want to have status code of the response set to 200(successful). I found many examples where people use MemoryStream and FileStreamResult to get proper file from the server but it doesn't work for me.
Unfortunately I always get message in the browser which is "Unexpected error", although the serwer code works without any exceptions. I checked details of the request in a browser and it has status code "Bad request" (400).
My code:
[HttpGet()]
public async Task<IActionResult> Download()
{
string jsonData = "example data";
byte[] byteArray = Encoding.UTF8.GetBytes(jsonData);
var stream = new MemoryStream(byteArray);
var result = new FileStreamResult(stream, new MediaTypeHeaderValue("text/json"))
{
FileDownloadName = "testFile.json"
};
return result;
}
[HttpGet()]
public async Task<HttpResponseMessage> Download()
{
string jsonData = "example data";
byte[] byteArray = Encoding.UTF8.GetBytes(jsonData);
var result = new HttpResponseMessage(HttpStatusCode.OK) {Content = new ByteArrayContent(byteArray)};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue(Inline) { FileName = "testFile.json" };
return result;
}
I'm trying to download a closedXml excel file in a webapi/angularjs application.
I'm returning the data from the webapi controller on the server using:
HttpResponseMessage result = new HttpResponseMessage();
result = Request.CreateResponse(HttpStatusCode.OK);
MemoryStream stream = GetStream(workbook);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Download.xlsx"
};
return result;
and then saving it on the client using:
$scope.openExcel = function (data, status, headers, deferred) {
var type = headers('Content-Type');
var disposition = headers('Content-Disposition');
if (disposition) {
var match = disposition.match(/.*filename=\"?([^;\"]+)\"?.*/);
if (match[1])
defaultFileName = match[1];
}
defaultFileName = defaultFileName.replace(/[<>:"\/\\|?*]+/g, '_');
var blob = new Blob([data], { type: type });
saveAs(blob, defaultFileName);
Excel says the file is in a different format than specified by the extension and then doesn't open properly.
On projects I work on, I make a Controller for files(not an ApiController)
public class FilesController : Controller
{
public FileResult GetFile(/*params*/)
{
// get fileBytes
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
return base.File(fileBytes, contentType, "Download.xlsx");
}
}
and then from angular, I open the file like this
$window.open("/Files/GetFile/" /*+ params*/);