How to download file using asp.net core? - c#

I use two actions to download a file. no errors display, but no files also get downloaded!!
first action
[HttpGet]
public IActionResult DownloadDoc()
{
string DocPath = "wwwroot/Uploads/5c92f430-4aef-41c8-b201-853597935771.jpg";
var image = System.IO.File.OpenRead(DocPath);
return File(image, "image/jpg");
}
second action
[HttpGet]
public IActionResult DownloadDoc()
{
string DocPath = "wwwroot/Uploads/5c92f430-4aef-41c8-b201-853597935771.jpg";
string fileName = "blablabla";
var net = new WebClient();
var data = net.DownloadData(DocPath);
var content = new MemoryStream(data);
var contentType = "APPLICATION/octet-stream";
return File(content, contentType,fileName);
}
Any one of them returns nothing, however, during tracing file is found.
How can I get a save dialog box to ask me where I would like to save my file on my local machine?

Related

URI formats are not supported when read file into bytes?

I am making API in asp.net Web API framework. I want to read file and convert it into bytes and return it to client.But when reading the file, exception occurs, URL format is not supported?
URL with fileName is send by client.I want to get the file from this URL and convert it into bytes. Tell me about , how i do this?
[Route("api/product/v1/displayimage")]
[AllowAnonymous]
[HttpPost]
//[GZipCompression]
public async Task<byte[]> DisplayImage([FromBody] FilesVM model)
{
try
{
var UrlBase = Url.Content(model.BaseURL);
//var UrlBase = Url.Content("~/Images/Users/5-signs-march14");
// MemoryStream workStream = new MemoryStream();
//string contentType = MimeMapping.GetMimeMapping(fileName);
byte[] byteInfo = System.IO.File.ReadAllBytes(UrlBase);
return await Task.FromResult(byteInfo);
}
catch (Exception ex)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
Url.Content only returns a string (http://localhost/Image...). If you want the actual content you will have to download it. Here's an example:
using (var client = new WebClient())
{
return await Task.FromResult(client.DownloadData(Url.Content("~/Images/Users/5-signs-march14")));
}

how to call web api to download document to website directory using webclient

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
}

download file from asp.net web api

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;
}

Download excel file using webapi and angularjs

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*/);

ASP.Net Core Content-Disposition attachment/inline

I am returning a file from a WebAPI controller. The Content-Disposition header value is automatically set to "attachment". For example:
Disposition: attachment; filename="30956.pdf"; filename*=UTF-8''30956.pdf
When it is set to attachment the browser will ask to save file instead of opening it. I would like it to open it.
How can I set it to "inline" instead of "attachment"?
I am sending the file using this method:
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
var fileContentResult = new FileContentResult(File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
// I need to delete file after me
System.IO.File.Delete(filename);
return fileContentResult;
}
The best way I have found is to add the content-disposition headers manually.
private IActionResult GetFile(int id)
{
var file = $"folder/{id}.pdf";
// Response...
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
{
FileName = file,
Inline = displayInline // false = prompt the user for downloading; true = browser to try to show the file inline
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return File(System.IO.File.ReadAllBytes(file), "application/pdf");
}
With version 2.0.0 of AspNetCore and AspNetCore.Mvc, I found none of the previous answers to be acceptable. For me, simply ommitting the filename argument to File was enough to trigger an inline content disposition.
return File(fileStream, contentType, fileName); // attachment
return File(fileStream, contentType); // inline
Update
In .NET 6, set the Content-Disposition header to inline or attachment by adding it to the response header:
// inline
Response.Headers.Add("Content-Disposition", "inline");
return File(fileStream, contentType);
// attachment
Response.Headers.Add("Content-Disposition", "attachment;filename=some.txt");
return File(fileStream, contentType);
You can override the default FileContentResult class so you can use it in your code with minimal changes:
public class InlineFileContentResult : FileContentResult
{
public InlineFileContentResult(byte[] fileContents, string contentType)
: base(fileContents, contentType)
{
}
public override Task ExecuteResultAsync(ActionContext context)
{
var contentDispositionHeader = new ContentDispositionHeaderValue("inline");
contentDispositionHeader.SetHttpFileName(FileDownloadName);
context.HttpContext.Response.Headers.Add(HeaderNames.ContentDisposition, contentDispositionHeader.ToString());
FileDownloadName = null;
return base.ExecuteResultAsync(context);
}
}
The same can be done for the FileStreamResult:
public class InlineFileStreamResult : FileStreamResult
{
public InlineFileStreamResult(Stream fileStream, string contentType)
: base(fileStream, contentType)
{
}
public override Task ExecuteResultAsync(ActionContext context)
{
var contentDispositionHeader = new ContentDispositionHeaderValue("inline");
contentDispositionHeader.SetHttpFileName(FileDownloadName);
context.HttpContext.Response.Headers.Add(HeaderNames.ContentDisposition, contentDispositionHeader.ToString());
FileDownloadName = null;
return base.ExecuteResultAsync(context);
}
}
Instead of returning a FileContentResult or FileStreamResult, just return InlineFileContentResult or InlineFileStreamResult. F.e.:
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
return new InlineFileContentResult(File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
}
Warning
As pointed out by makman99, do not use the ContentDisposition class for generating the header value as it will insert new-lines in the header-value for longer filenames.
Given you don't want to read the file in memory at once in a byte array (using the various File(byte[]...) overloads or using FileContentResult), you can either use the File(Stream, string, string) overload, where the last parameter indicates the name under which the file will be presented for download:
return File(stream, "content/type", "FileDownloadName.ext");
Or you can leverage an existing response type that supports streaming, such as a FileStreamResult, and set the content-disposition yourself. The canonical way to do this, as demonstrated in the FileResultExecutorBase, is to simply set the header yourself on the response, in your action method:
// Set up the content-disposition header with proper encoding of the filename
var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName("FileDownloadName.ext");
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
// Return the actual filestream
return new FileStreamResult(#"path\to\file", "content/type");
As File() would ignore Content-Disposition I used this:
Response.Headers[HeaderNames.ContentDisposition] = new MimeKit.ContentDisposition { FileName = fileName, Disposition = MimeKit.ContentDisposition.Inline }.ToString();
return new FileContentResult(System.IO.File.ReadAllBytes(filePath), "application/pdf");
and it works :-)
None of these solutions worked for me. The only thing that worked for me was updating the Cors of the backend:
services.AddCors(o => o.AddPolicy("MyPolicy", b =>
{
b.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition");
}));
so the header would be exposed. After this, I didn't need to add any additional header to the response.
And If you don't want to update your Startup.cs you can allow the header manually for that response:
HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
HttpContext.Response.Headers.Add("Content-Disposition", <your_header_value>);
try it with HttpResponseMessage
public IActionResult GetDocument(int id)
{
var filename = $"folder/{id}.pdf";
Response.Headers["Content-Disposition"] = $"inline; filename={id}.pdf";
var fileContentResult = new FileContentResult(System.IO.File.ReadAllBytes(filename), "application/pdf")
{
FileDownloadName = $"{id}.pdf"
};
// I need to delete file after me
System.IO.File.Delete(filename);
return fileContentResult;
}
Based on Ashley Lee's response but using ASP.Net Core stuff which solve problems for some file name patterns. Note that inline is the default content-disposition, so if you don't need to specify the filename (will be suggested if the user hit save on his browser) you can simply omit the content-disposition as suggested by Jonathan Wilson.
private IActionResult GetFile(int id)
{
var file = $"folder/{id}.pdf";
// Response...
var cd = new ContentDispositionHeaderValue("inline");
cd.SetHttpFileName(file);
Response.Headers[HeaderNames.ContentDisposition] = cd.ToString();
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return File(System.IO.File.ReadAllBytes(file), "application/pdf");
}
For ASP.NET Core, there doesn't seem to be any built-in way to return a file with 'Content-Disposition: inline' and filename. I created the following helper class that works very well. Tested with .NET Core 2.1.
public class InlineFileActionResult : Microsoft.AspNetCore.Mvc.IActionResult
{
private readonly Stream _stream;
private readonly string _fileName;
private readonly string _contentType;
private readonly int _bufferSize;
public InlineFileActionResult(Stream stream, string fileName, string contentType,
int bufferSize = DefaultBufferSize)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
_fileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
_contentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
if (bufferSize <= 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize,
"Buffer size must be greater than 0");
_bufferSize = bufferSize;
}
public async Task ExecuteResultAsync(Microsoft.AspNetCore.Mvc.ActionContext context)
{
using (_stream)
{
var response = context.HttpContext.Response;
response.Headers[HeaderNames.ContentType] = _contentType;
response.Headers[HeaderNames.ContentLength] = _stream.Length.ToString();
response.Headers[HeaderNames.ContentDisposition] =
new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue(
System.Net.Mime.DispositionTypeNames.Inline) {FileName = _fileName}.ToString();
await _stream.CopyToAsync(response.Body, _bufferSize, context.HttpContext.RequestAborted);
}
}
public const int DefaultBufferSize = 81920;
}
To use, return the class from the controller (whose return method must be IActionResult). An example is shown below:
[HttpGet]
public IActionResult Index()
{
var filepath = "C:\Path\To\Document.pdf";
return new InlineFileActionResult(new FileStream(filepath, FileMode.Open),
Path.GetFileName(filepath), "application/pdf");
}
This simply works for me in asp.net core 5.0 and hopefully this will work for previous versions too, as I was using same in asp.net 4.8
Response.ContentType = "application/pdf";
Response.Headers.Add("pragma", "no-cache, public");
Response.Headers.Add("cache-control", "private, nocache, must-revalidate, maxage=3600");
Response.Headers.Add("content-disposition", "inline;filename=" + fileName);
return File(bytes, "application/pdf");
An Asp.Net MVC approach using a similar approach to #ashley-lee
Note: Chrome downloads the attachment. See Ctrl-J list. But, if the user chooses 'Open' it will open 'in browser', a user would have to choose 'Open in System Viewer'. For example PDF signature fields are not visible in Browser based PDF viewers.
[HttpGet]
public ActionResult GenericForm()
{
return new DownloadFileAsAttachmentResult(#"GenericForm.pdf", #"\Content\files\GenericForm.pdf", "application/pdf");
}
public class DownloadFileAsAttachmentResult : ActionResult
{
private string _filenameWithExtension { get; set; }
private string _filePath { get; set; }
private string _contentType { get; set; }
// false = prompt the user for downloading; true = browser to try to show the file inline
private const bool DisplayInline = false;
public DownloadFileAsAttachmentResult(string FilenameWithExtension, string FilePath, string ContentType)
{
_filenameWithExtension = FilenameWithExtension;
_filePath = FilePath;
_contentType = ContentType;
}
public override void ExecuteResult(ControllerContext context)
{
HttpResponseBase response = context.HttpContext.Response;
response.Buffer = false;
response.ContentType = _contentType;
response.AddHeader("Content-Disposition", "attachment; filename=" + _filenameWithExtension); // force download
response.AddHeader("X-Content-Type-Options", "nosniff");
response.TransmitFile(_filePath);
}
}
Note that when the file can't be opened in the client's browser it will be downloaded. To assure filenames with special characters are correctly handled I found the following method to be most robust to set the Content-Disposition header:
var contentDisposition = new ContentDispositionHeaderValue("inline");
contentDisposition.SetHttpFileName("éáëí.docx");
Response.Headers.Add(HeaderNames.ContentDisposition, contentDisposition.ToString());
ContentDispositionHeaderValue is located in namespace Microsoft.Net.Http.Headers.
I followed #myro's answer. For my .net core 3.1 web API, I found the ContentDisposition class and constants in the System.Net.Mime namespace.
var result = new FileContentResult(System.IO.File.ReadAllBytes(filePath), mimeType);
var dispositionType = asAttachment
? System.Net.Mime.DispositionTypeNames.Attachment
: System.Net.Mime.DispositionTypeNames.Inline;
Response.Headers[HeaderNames.ContentDisposition] = new
System.Net.Mime.ContentDisposition { FileName = "file.text",
DispositionType = dispositionType }.ToString();
return result;
Try this code in classic Razor page (tested in ASP.NET Core 3.1). For forced download is used query param "?download=1". As you see, necessary is add parameter "attachment" into the "Content-Disposition" header for the specific position.
public class FilesModel : PageModel
{
IWebHostEnvironment environment;
public FilesModel(IWebHostEnvironment environment)
{
this.environment = environment;
}
public PhysicalFileResult OnGet()
{
// Query params
string fileName = Request.Query["filename"];
bool forcedDownload = Request.Query["download"] == "1";
// File Path
string filePath = Path.Combine(env.ContentRootPath, "secret-files", fileName);
if (!System.IO.File.Exists(filePath)) return null; // File not exists
// Make sure that the user has permissions on the file...
// File info
string mime = "image/png"; // Choose the right mime type...
long fileSize = new FileInfo(filePath).Length;
string sendType = forcedDownload ? "attachment" : "inline";
// Headers
Response.Headers.Add("Content-Disposition", $"{sendType};filename=\"{fileName}\"");
Response.Headers.Add("Content-Length", fileSize.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
// Result
return new PhysicalFileResult(filePath, mime);
}
}

Categories