I have a simple form on Angular , where I upload a file and a description.
constructor(private http: HttpClient) { }
upload(files) {
if (files.length === 0)
return;
const formData: FormData = new FormData();
var filedesc = this.description;
for (let file of files) {
formData.append(file.name, file);
formData.append("Description", filedesc);
}
const uploadReq = new HttpRequest('POST', `api/upload`, formData, {
reportProgress: true,
});
In the controller, I get only the file name.
[HttpPost, DisableRequestSizeLimit]
public ActionResult UploadFile()
{
try
{
var fileContent = Request.Form.Files[0];
string folderName = "Upload";
var contenttype = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
string webRootPath = _hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (fileContent.Length > 0)
{
if (fileContent.ContentType == contenttype)
{
string fileName = ContentDispositionHeaderValue.Parse(fileContent.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(newPath, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
fileContent.CopyTo(stream);
}
}
else
{
return Json("Wrong File Type.");
}
My question is how to receive description string in this case? Or is it bad that I append file and description in one request?
Like said Aman B before, add a parameter and use this code
foreach (string key in Request.Form.Keys)
{
description = key;
}
You have to add a parameter to your action method to receive the description:
//If you are expecting a single file description
public ActionResult UploadFile(string description)
{
//Logic..
}
//If you are expecting multiple file descriptions
public ActionResult UploadFile(IEnumerable<string> description)
{
//Logic..
}
It is ok to send form data with files in the same request. This is what the "multipart/form-data" content-type header is used for.
Related
I need to upload files from a client to a server. But I found a problem, I can not filter the files. I need to discard files that do not have a suitable extension . If the file does not have the right extension then I will not load it to the server.
I tried to filter, but I don't have any idea how to write this. Can anyone help me with this part?
public enum FileExtension
{
Unknown = 0,
Doc = 1,
Rtf = 2,
Html = 3
}
public static class FileExtensionExtensions {
public static string GetExtension(this FileExtension ext) {
switch (ext) {
case FileExtension.Doc:
return ".doc";
case FileExtension.Html:
return ".html";
case FileExtension.Rtf:
return ".rtf";
default:
throw new ArgumentException();
}
}
public static FileExtension GetFileExtension(string ext) {
if (string.IsNullOrWhiteSpace(ext)) throw new ArgumentException("message", nameof(ext));
ext = ext.Trim('.').ToLower();
switch (ext) {
case ".doc":
return FileExtension.Doc;
case ".html":
return FileExtension.Html;
case ".rtf":
return FileExtension.Rtf;
default:
throw new ArgumentException();
}
}
}
[Route("api")][ApiController]
public class UploadDownloadController:
ControllerBase {
private IHostingEnvironment _hostingEnvironment;
public UploadDownloadController(IHostingEnvironment environment) {
_hostingEnvironment = environment;
}
[HttpPost][Route("upload")]
public async Task < IActionResult > Upload(IFormFile file) {
string fileExtension = Path.GetExtension(file.FileName).Trim('.');
if (file.Length > 0) {
string dir = Folder.GetAllPath(Path.Combine(Folder.GetAllPath, fileExtension));
string filePath = Path.Combine(dir, file.FileName);
using(var fileStream = new FileStream(filePath, FileMode.Create)) {
await file.CopyToAsync(fileStream);
}
}
return Ok();
}
[HttpGet][Route("download")]
public async Task < IActionResult > Download([FromQuery] string file) {
string fileExtension = Path.GetExtension(file).Trim('.');
string dir = Folder.GetAllPath(Path.Combine(Folder.GetAllPath, fileExtension));
string filePath = Path.Combine(dir, file);
if (!System.IO.File.Exists(filePath)) return NotFound();
var memory = new MemoryStream();
using(var stream = new FileStream(filePath, FileMode.Open)) {
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetContentType(filePath), file);
}
[HttpGet][Route("files")]
public IActionResult Files() {
var result = new List < string > ();
if (Directory.Exists(Folder.GetAllPath("txt"))) {
var files = Directory.GetFiles(Folder.GetAllPath("txt")).Select(fn = >Path.GetFileName(fn));
result.AddRange(files);
}
return Ok(result);
}
private string GetContentType(string path) {
var provider = new FileExtensionContentTypeProvider();
string contentType;
if (!provider.TryGetContentType(path, out contentType)) {
contentType = "application/octet-stream";
}
return contentType;
}
}
}
Your first problem here is that the code you are showing is server side code. That means you can only detect the file type after the file (IFormFile) has been uploaded. I assume you want to stop the file selection and file upload before the files are uploaded. You will have to do this on the client side with JavaScript or a client side framework like Angular.
A pointer: When defining the file input in your Html DOM you can specify which extensions to allow. E.g. .txt, .csv You can extend this to mime types as well. E.g. image/jpg, image/gif. Note that this is not foolproof and can only be described as best effort. Client side validation will still be needed.
<input type="file" accept=".txt, .csv" />
<input type="file" accept="image/jpg, image/gif">
<input type="file" accept=".txt, .csv, image/jpg, image/gif">
I've been trying to download .xml file, but sadly unsuccesfully.
From angular side I am sending *.xml file. In .NET side I take it to process and create a new *.xml file. And I need to download that new file, however I can't really find out how to workaround it.
this is my file-component.ts:
OnSubmit(value, File) {
const params1: FormData = new FormData();
params1.append('File', this.fileToUpload, this.fileToUpload.name);
params1.append('ProcessingMode', value.processingMode);
params1.append('StartDate', value.startDate.formatted);
const params = {
'File': this.fileToUpload,
'ProcessingMode': value.processingMode,
'StartDate': value.startDate.formatted
};
this.mapsConfigurationService.postFile(value, this.fileToUpload, value.startDate.formatted)
.subscribe((res: any) => {
this.downloadFile(res, 'xml'); debugger;
this.xmlProcessing = false;
},
(err) => {
if (err.status === 401) {
// this.router.navigate(['unauthorized']);
} else {
this.xmlProcessing = false;
}
});
downloadFile(data, type) {
const fileName = 'test';
var contentType;
if (type === 'xml') {
contentType = 'text/xml';
}
var blob = new Blob([data._body], { type: contentType });
const dwldLink = document.createElement('a');
const url = URL.createObjectURL(blob);
const isSafariBrowser = navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1;
if (isSafariBrowser) {
dwldLink.setAttribute('target', '_blank');
}
const fullFileName = fileName + '.' + type;
dwldLink.setAttribute('href', url);
dwldLink.setAttribute('download', fullFileName);
dwldLink.style.visibility = 'hidden';
document.body.appendChild(dwldLink);
dwldLink.click();
document.body.removeChild(dwldLink);}
this is service.ts
postFile(value: any, fileToUpload: File, startDate) {
const formData: FormData = new FormData();
formData.append('File', fileToUpload, fileToUpload.name);
formData.append('ProcessingMode', value.processingMode);
formData.append('StartDate', '2015-05-23');
return this.http
.post(this.Url, formData);
}
and this is backend side:
[HttpPost, DisableRequestSizeLimit]
public ActionResult UploadFile()
{
try
{
var xml = Request.Form.Files["File"].ToString();
var httpRequest = HttpContext.Request.Form;
var postedFile = httpRequest.Files["File"];
string outputFile = Request.Form["info"].ToString();
var startDate = Request.Form["StartDate"];
var file = httpRequest.Files[0];
string fullPath = "";
string folderName = "Upload";
string antFile = #"C:\ant.bat";
string build = #"C:\build.xml";
string rootPath = #"C:\Users";
string newPath = Path.Combine(rootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (file.Length > 0)
{
string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
fullPath = Path.Combine(newPath, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
}
return PhysicalFile(#"C:\Book1.xml", "application/xml", "Book1.xml");
}
catch (System.Exception ex)
{
return StatusCode(500);
}
}
I get error 500 and I do understand that the problem is with RequestHeaders but I can't figure out the way to solve it and in which side
for downloading for downloading any file... I am use this code in backend
and make and request the code from angular by normal http request
var myFile :: your file
if (System.IO.File.Exists (myFile.Path)) {// to know if the file is Exist or not
//Process File Here ...
} else {
return Json ("NotFound");
}
string contentType = "application/xml";
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult (System.IO.File.ReadAllBytes (myFile.Path), contentType) {
FileDownloadName = $"{myFile.Title }" // + myFile.Extension
};
// System.IO.File.Delete (myFile.Path); //if you want to delete the file after download
return result;
I am using C# ASP.NET code and trying to download file on the post request of a form. here is my sample code.
[HttpPost]
public ActionResult PostMethodName(PostModel inputModel)
{
if (ModelState.IsValid)
{
//other code is removed.
//Writing this for the test
//Download Method call
DownloadCertificate("This is the test file to download.");
var statusHtml = RenderViewToString("Status",
new ErrorMsgModel
{
IsSuccess = true,
ErrorDesc = "desc"
});
return Json(new { IsSuccess = true, ErrorDescription =
statusHtml}, JsonRequestBehavior.AllowGet);
}
var statusHtml1 = RenderViewToString("Status",
new ErrorMsgModel
{
IsSuccess = false,
ErrorDesc = "desc"
});
statusHtml1 = statusHtml1.Replace("'", "\\'");
statusHtml1 = statusHtml1.Replace(Environment.NewLine, "");
return Json(new { IsSuccess = false, ErrorDescription = statusHtml1
}, JsonRequestBehavior.AllowGet);
}
Download method which is called from this method.
public ActionResult DownloadCertificate(string content)
{
//Certificate Download
const string fileType = "application/pkcs10";
string fileName = "Certificate" + DateTime.Today.ToString(#"yyyy-MM-dd") + ".csr";
var fileContent = String.IsNullOrEmpty(contrnt) ? "" : contrnt;
byte[] fileContents = Encoding.UTF8.GetBytes(fileContent);
var result = new FileContentResult(fileContents, fileType) { FileDownloadName = fileName };
return result;
}
file download is not working, post functionality is working as desired.
[HttpPost]
public ActionResult DownloadCertificate(PostModel inputModel, string content)
{
if(!ModelState.IsValid){return Json(new {Success=false,//error descr})}
//Certificate Download
const string fileType = "application/pkcs10";
string fileName = "Certificate" + DateTime.Today.ToString(#"yyyy-MM-dd") + ".csr";
var fileContent = String.IsNullOrEmpty(contrnt) ? "" : contrnt;
byte[] fileContents = Encoding.UTF8.GetBytes(fileContent);
var result = new FileContentResult(fileContents, fileType) { FileDownloadName = fileName };
return result;
}
In your previous code you don`t use DownloadCertificate result, you simly execute it.
Your DownloadCertificate method returns a value, but you never use the return value in your PostMethodName method.
Given that you are returning json from that method I would suggest that you return a direct link to the file result in the response. The consuming client can then initiate the download. Something like:
return Json(new { IsSuccess = true, Location = Url.Action("DownloadContent")});
Alternatively you could consider a more restful approach and return a 302 response from the post action:
if (ModelState.IsValid)
{
// you code here
return RedirectToAction("Controller", "DownloadContent", new {content = "myContent"});
}
This may well proceed with the download transparently depending on your client whilst keeping to the Post-Redirect-Get pattern
I have some data to save into a database.
I have created a web api post method to save data. Following is my post method:
[Route("PostRequirementTypeProcessing")]
public IEnumerable<NPAAddRequirementTypeProcessing> PostRequirementTypeProcessing(mdlAddAddRequirementTypeProcessing requTypeProcess)
{
mdlAddAddRequirementTypeProcessing rTyeProcessing = new mdlAddAddRequirementTypeProcessing();
rTyeProcessing.szDescription = requTypeProcess.szDescription;
rTyeProcessing.iRequirementTypeId = requTypeProcess.iRequirementTypeId;
rTyeProcessing.szRequirementNumber = requTypeProcess.szRequirementNumber;
rTyeProcessing.szRequirementIssuer = requTypeProcess.szRequirementIssuer;
rTyeProcessing.szOrganization = requTypeProcess.szOrganization;
rTyeProcessing.dIssuedate = requTypeProcess.dIssuedate;
rTyeProcessing.dExpirydate = requTypeProcess.dExpirydate;
rTyeProcessing.szSignedBy = requTypeProcess.szSignedBy;
rTyeProcessing.szAttachedDocumentNo = requTypeProcess.szAttachedDocumentNo;
if (String.IsNullOrEmpty(rTyeProcessing.szAttachedDocumentNo))
{
}
else
{
UploadFile();
}
rTyeProcessing.szSubject = requTypeProcess.szSubject;
rTyeProcessing.iApplicationDetailsId = requTypeProcess.iApplicationDetailsId;
rTyeProcessing.iEmpId = requTypeProcess.iEmpId;
NPAEntities context = new NPAEntities();
Log.Debug("PostRequirementTypeProcessing Request traced");
var newRTP = context.NPAAddRequirementTypeProcessing(requTypeProcess.szDescription, requTypeProcess.iRequirementTypeId,
requTypeProcess.szRequirementNumber, requTypeProcess.szRequirementIssuer, requTypeProcess.szOrganization,
requTypeProcess.dIssuedate, requTypeProcess.dExpirydate, requTypeProcess.szSignedBy,
requTypeProcess.szAttachedDocumentNo, requTypeProcess.szSubject, requTypeProcess.iApplicationDetailsId,
requTypeProcess.iEmpId);
return newRTP.ToList();
}
There is a field called 'szAttachedDocumentNo' which is a document that's being saved in the database as well.
After saving all data, I want the physical file of the 'szAttachedDocumentNo' to be saved on the server. So i created a method called "UploadFile" as follows:
[HttpPost]
public void UploadFile()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
// Get the uploaded file from the Files collection
var httpPostedFile = HttpContext.Current.Request.Files["UploadedFile"];
if (httpPostedFile != null)
{
// Validate the uploaded image(optional)
string folderPath = HttpContext.Current.Server.MapPath("~/UploadedFiles");
//string folderPath1 = Convert.ToString(ConfigurationManager.AppSettings["DocPath"]);
//Directory not exists then create new directory
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// Get the complete file path
var fileSavePath = Path.Combine(folderPath, httpPostedFile.FileName);
// Save the uploaded file to "UploadedFiles" folder
httpPostedFile.SaveAs(fileSavePath);
}
}
}
Before running the project, i debbugged the post method, so when it comes to "UploadFile" line, it takes me to its method.
From the file line, it skipped the remaining lines and went to the last line; what means it didn't see any file.
I am able to save everything to the database, just that i didn't see the physical file in the specified location.
Any help would be much appreciated.
Regards,
Somad
Makes sure the request "content-type": "multipart/form-data" is set
[HttpPost()]
public async Task<IHttpActionResult> UploadFile()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
MultipartMemoryStreamProvider provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.Contents != null && provider.Contents.Count == 0)
{
return BadRequest("No files provided.");
}
foreach (HttpContent file in provider.Contents)
{
string filename = file.Headers.ContentDisposition.FileName.Trim('\"');
byte[] buffer = await file.ReadAsByteArrayAsync();
using (MemoryStream stream = new MemoryStream(buffer))
{
// save the file whereever you want
}
}
return Ok("files Uploded");
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
How do I get Content-Disposition parameters I returned from WebAPI controller using WebClient?
WebApi Controller
[Route("api/mycontroller/GetFile/{fileId}")]
public HttpResponseMessage GetFile(int fileId)
{
try
{
var file = GetSomeFile(fileId)
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new MemoryStream(file));
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = file.FileOriginalName;
/********* Parameter *************/
response.Content.Headers.ContentDisposition.Parameters.Add(new NameValueHeaderValue("MyParameter", "MyValue"));
return response;
}
catch(Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
Client
void DownloadFile()
{
WebClient wc = new WebClient();
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
wc.DownloadDataAsync(new Uri("api/mycontroller/GetFile/18"));
}
void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
WebClient wc=sender as WebClient;
// Try to extract the filename from the Content-Disposition header
if (!String.IsNullOrEmpty(wc.ResponseHeaders["Content-Disposition"]))
{
string fileName = wc.ResponseHeaders["Content-Disposition"].Substring(wc.ResponseHeaders["Content-Disposition"].IndexOf("filename=") + 10).Replace("\"", ""); //FileName ok
/****** How do I get "MyParameter"? **********/
}
var data = e.Result; //File OK
}
I'm returning a file from WebApi controller, I'm attaching the file name in the response content headers, but also I'd like to return an aditional value.
In the client I'm able to get the filename, but how do I get the aditional parameter?
If you are working with .NET 4.5 or later, consider using the System.Net.Mime.ContentDisposition class:
string cpString = wc.ResponseHeaders["Content-Disposition"];
ContentDisposition contentDisposition = new ContentDisposition(cpString);
string filename = contentDisposition.FileName;
StringDictionary parameters = contentDisposition.Parameters;
// You have got parameters now
Edit:
otherwise, you need to parse Content-Disposition header according to it's specification.
Here is a simple class that performs the parsing, close to the specification:
class ContentDisposition {
private static readonly Regex regex = new Regex(
"^([^;]+);(?:\\s*([^=]+)=((?<q>\"?)[^\"]*\\k<q>);?)*$",
RegexOptions.Compiled
);
private readonly string fileName;
private readonly StringDictionary parameters;
private readonly string type;
public ContentDisposition(string s) {
if (string.IsNullOrEmpty(s)) {
throw new ArgumentNullException("s");
}
Match match = regex.Match(s);
if (!match.Success) {
throw new FormatException("input is not a valid content-disposition string.");
}
var typeGroup = match.Groups[1];
var nameGroup = match.Groups[2];
var valueGroup = match.Groups[3];
int groupCount = match.Groups.Count;
int paramCount = nameGroup.Captures.Count;
this.type = typeGroup.Value;
this.parameters = new StringDictionary();
for (int i = 0; i < paramCount; i++ ) {
string name = nameGroup.Captures[i].Value;
string value = valueGroup.Captures[i].Value;
if (name.Equals("filename", StringComparison.InvariantCultureIgnoreCase)) {
this.fileName = value;
}
else {
this.parameters.Add(name, value);
}
}
}
public string FileName {
get {
return this.fileName;
}
}
public StringDictionary Parameters {
get {
return this.parameters;
}
}
public string Type {
get {
return this.type;
}
}
}
Then you can use it in this way:
static void Main() {
string text = "attachment; filename=\"fname.ext\"; param1=\"A\"; param2=\"A\";";
var cp = new ContentDisposition(text);
Console.WriteLine("FileName:" + cp.FileName);
foreach (DictionaryEntry param in cp.Parameters) {
Console.WriteLine("{0} = {1}", param.Key, param.Value);
}
}
// Output:
// FileName:"fname.ext"
// param1 = "A"
// param2 = "A"
The only thing that should be considered when using this class is it does not handle parameters (or filename) without a double quotation.
Edit 2:
It can now handle file names without quotations.
You can parse out the content disposition using the following framework code:
var content = "attachment; filename=myfile.csv";
var disposition = ContentDispositionHeaderValue.Parse(content);
Then just take the pieces off of the disposition instance.
disposition.FileName
disposition.DispositionType
With .NET Core 3.1 and more the most simple solution is:
using var response = await Client.SendAsync(request);
response.Content.Headers.ContentDisposition.FileName
The value is there I just needed to extract it:
The Content-Disposition header is returned like this:
Content-Disposition = attachment; filename="C:\team.jpg"; MyParameter=MyValue
So I just used some string manipulation to get the values:
void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
WebClient wc=sender as WebClient;
// Try to extract the filename from the Content-Disposition header
if (!String.IsNullOrEmpty(wc.ResponseHeaders["Content-Disposition"]))
{
string[] values = wc.ResponseHeaders["Content-Disposition"].Split(';');
string fileName = values.Single(v => v.Contains("filename"))
.Replace("filename=","")
.Replace("\"","");
/********** HERE IS THE PARAMETER ********/
string myParameter = values.Single(v => v.Contains("MyParameter"))
.Replace("MyParameter=", "")
.Replace("\"", "");
}
var data = e.Result; //File ok
}
As #Mehrzad Chehraz said you can use the new ContentDisposition class.
using System.Net.Mime;
// file1 is a HttpResponseMessage
FileName = new ContentDisposition(file1.Content.Headers.ContentDisposition.ToString()).FileName