characters replaced in the filename when downloaded - c#

Working o an webapi application with angularJS as UI.
When I download a file to my desktop from my application. The name is changed replacing some characters like 'é','ü' with characters like these é.
The code used to donwload the file is the following.
[HttpGet, Route("{documentid}/raw", Order = 5)]
public IHttpActionResult GetDocumentRaw(string documentid)
{
var service = ResolveService<IDocumentService>();
var raw = service.GetRaw(documentid);
if (raw.Data == null)
{
return NotFound();
}
var fileName = raw.FileName?.GetCleanFileName();
var contentType = raw.ContentType;
if (String.IsNullOrWhiteSpace(fileName))
{
fileName = "rawdata";
}
if (String.IsNullOrWhiteSpace(contentType))
{
contentType = "application/octet-stream";
}
var response = Request.CreateResponse(HttpStatusCode.OK);
Stream stream = new MemoryStream(raw.Data);
response.Content = new StreamContent(stream);
response.Content.Headers.Remove("content-type");
response.Content.Headers.Add("content-type", contentType);
response.Content.Headers.Add("content-disposition", "inline; filename=\"" + fileName + "\"");
response.Content.Headers.Add("content-length", stream.Length.ToString());
response.Content.Headers.Remove("x-filename");
response.Content.Headers.Add("x-filename", fileName);
return ResponseMessage(response);
}
In the UI side, this function is used to download the file.
$download: function(propertyTypeKey) {
$http
.get(this.url + '/raw', {
responseType: 'arraybuffer',
params: {
propertyTypeKey: propertyTypeKey
}
})
.then(function (response) {
DownloadService.downloadFile(
response.data,
response.headers('content-type'),
response.headers('x-filename')
);
});
},
Is this something related to textformat encoding ? Should I add some configuration in the Http Response ? Or may be it is related to the configuration of the IIS Server ?
I firstly suspected my chrome navigator but I'm facing the same issue on IE.
Thank you.

Related

.stl file downloaded not opening or corrupt

I am having problem with .stl file download in c# Angular JS MVC.
While the file is downloading I am not able to open the file in 3d shape or another software supporting stl file extension.
vm.FileDownload = function (id, hash,filename) {
var defer = $q.defer();
threeShapeRepo.getFile(id, hash, filename).then(function (data) {
if (data !== null) {
var file = new Blob([data], {
type: 'application/stl'
});
var fileURL = URL.createObjectURL(file);
var link = document.createElement('a');
link.href = fileURL;
link.target = '_blank';
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
//window.URL.revokeObjectURL(url);
defer.resolve(data);
});
}
From repository.js
function getFile(id, hash, fileName) {
var params = {
'Id': id,
'Hash': hash,
'FileName': fileName
}
var url = 'https://' + window.location.host + '/api/' + 'ThreeShape' + '/' + 'getFileAsync';
return $http.post(url, params, { responseType: 'blob' }).then(function (data) {
if (data.data.Content.indexOf("validation failed")!==-1) {
logError(data.data.Content);
return null;
}
return data.data;
});
}
In c# back end code:
public async Task<string> getFileAsyncInternal(GetFile request)
{
var response = new GetThreeShapeCasesResponse();
IList<GetOneThreeShapeCaseResult> tempCaseList = new List<GetOneThreeShapeCaseResult>();
IRestResponse restResponse;
var retryCount = 0;
HttpStatusCode statusCode;
int numericStatusCode;
var requestUrl = "";
InitializeThreeShapeClient();
var restRequest = new RestRequest(Method.GET);
if (request.Hash != null)
{
requestUrl = threeShapeServerLocation + "api/cases/"+ request.Id+"/attachments/"+ request.Hash;
}
ThreeShapeAPIClient.BaseUrl = new Uri(requestUrl);
var getTokenOperation = await retreive3ShapeBearerTokenInternal();
var myToken = getTokenOperation.TokenValue;
var tokenString = "Bearer " + myToken;
restRequest.AddHeader("Authorization", tokenString);
restResponse = ThreeShapeAPIClient.Execute(restRequest);
numericStatusCode = (int)restResponse.StatusCode;
System.Web.HttpResponse httpresponse = System.Web.HttpContext.Current.Response; ;
if (numericStatusCode == 200)
{
var strResult = restResponse.Content;
//return strResult;
string fileName = request.FileName;
httpresponse.ClearContent();
httpresponse.Clear();
byte[] buffer = new byte[restResponse.Content.Length];
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
{
FileName = fileName,
Inline = true // false = prompt the user for downloading; true = browser to try to show the file inline
};
httpresponse.Headers.Add("Content-Disposition", "attachment;filename="+request.FileName+ ";filename*=UTF-8''" + request.FileName);
httpresponse.Headers.Add("Content-Length",Convert.ToString(restResponse.Content.Length));
httpresponse.Headers.Add("Contenty-Type", ((RestSharp.RestResponseBase)restResponse).ContentType);
httpresponse.Output.Write(restResponse.Content);
return restResponse.Content;
}
else if (retryCount == 0 && numericStatusCode == 401)
{
response.DidError = true;
response.ErrorMessage = restResponse.Content;
}
return restResponse.Content;
}
I have been struggling to make the downloaded file open. Any kind of help is deeply appreciated.
Response i am getting in content object is
I would start by comparing file contents of a clean copy and corrupt downloaded copy for clues. Is the downloaded copy really corrupt or is it actually empty? Do you see a pattern in the corrupt copies? etc Goodluck!

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

Weird filename when downloading file from Internet Explorer

I have a webapplication that is set up with a link, and when this link is pressed then a file is downloaded. The file location right now i is on my own HDD. It works when I download the file from Google Chrome, but when i use IE i get a weird scrambled filename. This is my code:
[HttpGet]
public HttpResponseMessage GetFile(string filename, string environment)
{
_requestLogger.LogRequest(Request);
var validateResult = new InputValidator().ValidateInput(filename);
if (!validateResult.IsValid)
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest, ReasonPhrase = validateResult.ErrorMessage };
}
filename = SanitizeFileName(filename);
var filePath = _settings.ResolveFilePath(environment, filename);
if (!File.Exists(filePath))
{
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
//Get file
var stream = new FileStream(filePath, FileMode.Open);
//Create result
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
//Add content to result
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
Here is a link to an image of the weird filename
I cant seem to figure out why this is happening. Can anyone help ?
Try to add the filename and content-length headers, something like this:
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = fileName.ToString()
};
response.Content.Headers.ContentLength=stream.Length.ToString();
I figured out what i had to do. I created this method and it solved my problem.
public static void AddProperFileNameHeadersIfIE(HttpContextBase httpContext, string fileName)
{
var browser = httpContext.Request.Browser;
if (browser != null && browser.Browser.Equals("ie", StringComparison.OrdinalIgnoreCase))
{
httpContext.Response.AppendHeader("Content-Disposition", "attachment; filename*=UTF-8''" + HttpUtility.UrlPathEncode(fileName) + "\"");
}
else
{
httpContext.Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + HttpUtility.UrlPathEncode(fileName) + "\"");
}
}

Valums Ajax File Upload works in IE but not in Firefox

I'm developing in ASP.NET MVC3 and I have the the code below for saving the file in Sql Server 2008, it works well for IE (I used IE9) but in Firefox I get the error "Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index", how should i fix this? thanks
[HttpPost]
public ActionResult FileUpload(string qqfile)
{
try
{
HttpPostedFileBase postedFile = Request.Files[0];
var stream = postedFile.InputStream;
App_MessageAttachment NewAttachment = new App_MessageAttachment
{
FileName = postedFile.FileName.ToString().Substring(postedFile.FileName.ToString().LastIndexOf('\\') + 1),
FilteContentType = postedFile.ContentType,
MessageId = 4,
FileData = new byte[postedFile.ContentLength]
};
postedFile.InputStream.Read(NewAttachment.FileData, 0, postedFile.ContentLength);
db.App_MessageAttachments.InsertOnSubmit(NewAttachment);
db.SubmitChanges();
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message }, "application/json");
}
return Json(new { success = true }, "text/html");
}
The Valums Ajax upload has 2 modes. If it recognizes that the browser supports HTML5 File API (which undoubtedly is the case with FireFox) it uses this API instead of using an enctype="multipart/form-data" request. So in your controller action you need to account for those differences and in the case of modern browsers that support HTML5 read the Request.InputStream directly:
[HttpPost]
public ActionResult FileUpload(string qqfile)
{
try
{
var stream = Request.InputStream;
var filename = Path.GetFileName(qqfile);
// TODO: not sure about the content type. Check
// with the documentation how is the content type
// for the file transmitted in the case of HTML5 File API
var contentType = Request.ContentType;
if (string.IsNullOrEmpty(qqfile))
{
// IE
var postedFile = Request.Files[0];
stream = postedFile.InputStream;
filename = Path.GetFileName(postedFile.FileName);
contentType = postedFile.ContentType;
}
var contentLength = stream.Length;
var newAttachment = new App_MessageAttachment
{
FileName = filename,
FilteContentType = contentType,
MessageId = 4,
FileData = new byte[contentLength]
};
stream.Read(newAttachment.FileData, 0, contentLength);
db.App_MessageAttachments.InsertOnSubmit(newAttachment);
db.SubmitChanges();
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message });
}
return Json(new { success = true }, "text/html");
}
The code might need some tweaking. I don't have time to test it right now but you get the idea: in the case of HTML5 enabled browser the file is written directly to the body of the request whereas for browsers that do not support the File API the file data is transmitted using the standard multipart/form-data encoding.

Categories