I have a solution for uploading a file and other data via an AJAX request to an MVC controller.
For a long time, this has been working fine, but today, I've been investigating issues people have been reporting with it, and now it appears to not be functioning at all; I am now getting a "Resource not found" error returned.
If I remove "data: fileData," from the AJAX call, then it is able to resolve the URL, but obviously, there is then no data to process.
Would someone be able to shed any light on why this could stop functioning in this way? I am at a loss to explain this.
In Short:
AJAX call no longer able to resolve controller method URL. No obvious recent changes made to this functionality in repository history.
Thank you,
Matthew.
UPDATE
For the sake of those interested, it turns out that the size of the specific PDF file I was given to test was the culprit all along...This solution still works, and can work for larger files by
adding the following to the web.config file:
<system.web>
<httpRuntime maxRequestLength="10240" />
<!--This allows larger files to be uploaded (10MB in KB)-->
</system.web>
...
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="10485760" />
<!--This allows larger files to be uploaded (10MB in Bytes)-->
</requestFiltering>
</security>
</system.webServer>
END UPDATE
In-page Javascript:
function SaveAttachmentData() {
var attachmentID = $('#attachmentrecordid').val();
var servID = $('#servrecordid').val();
var description = $('#txtattachmentdescription').val();
// Checking whether FormData is available in browser
if (window.FormData !== undefined) {
var fileUpload = $("#attachmentfile").get(0);
var files = fileUpload.files;
// Create FormData object
var fileData = new FormData();
// Looping over all files and add it to FormData object
for (var i = 0; i < files.length; i++) {
fileData.append(files[i].name, files[i]);
}
// Adding keys to FormData object
fileData.append('service_id', servID);
fileData.append('dataid', attachmentID);
fileData.append('description', description);
var validData = true;
if (!isNormalInteger(servID)){validData = false;}
if (servID < 1) {validData = false;}
if (files.length == 0 && $('#attachmentrecordid').val() < 1){validData = false;}
if (validData){ //All data is valid
$.ajax({
url: '/Service/AttachmentCreate',
type: "POST",
contentType: false, // Not to set any content header
processData: false, // Not to process data
data: fileData,
success: function (result) {
ReloadAttachmentTable();
},
error: function (err) {
ReloadAttachmentTable();
}
});
}
else{
//Data is not valid
alert('Invalid data entered!');
//ShowBannerMessageWarning('Warning!','Invalid data entered!');
}
} else {
alert("FormData is not supported.");
}
}
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AttachmentCreate()
{
//Upload file
string message = "";
DataModel dm = new DataModel();
Service.Attachment sa = new Service.Attachment();
//Get request data
string description = Request.Form["description"];
int servid = int.Parse(Request.Form["service_id"]);
HttpFileCollectionBase files = Request.Files;
//Only continue if a file was uploaded
if (files.Count > 0)
{
//Get the file
HttpPostedFileBase file = files[0];
string fname;
// Checking for Internet Explorer
if (Request.Browser.Browser.ToUpper() == "IE" || Request.Browser.Browser.ToUpper() == "INTERNETEXPLORER")
{
string[] testfiles = file.FileName.Split(new char[] { '\\' });
fname = testfiles[testfiles.Length - 1];
}
else
{
fname = file.FileName;
}
//Update attachment properties
sa.ID = servid;
sa.Description = description;
sa.Filename = fname; //Assign fname before the full path is added to it
//Get full filename from attachment class property
fname = sa.Filename_Structured;
// Get the complete folder path and store the file inside it.
fname = Path.Combine(ConfigurationManager.AppSettings["AttachmentFolderLocation"].ToString(), fname);
//Save file to server
file.SaveAs(fname);
//Create file attachment record against service
message = dm.CreateAttachmentData(ref sa);
message = message == "" ? "Success" : message;
}
else { message = "No file uploaded"; }
return Content(message);
}
Related
As mentioned in my Title, upon accessing Request.Form.Files, the application throws the exception
System.Io.InvalidDataException: "Multipart body length limit 16384 exceeded" in an Asp.Net Core 2.0.0 Web Application. The Application I am working on is an Asp.Net Core MVC Web Application. I am using Summernote as my WYSIWYG Text-Editor. There I enabled the Feature to upload Images (s. my Javascript). This worked fine, until I needed to implement another uploader to upload text files like .pdf, txt, doc.
The Error appears while using the summernote texteditor, either with the "standard" upload button or a custom pdf uplader button (s. C# Code). The Image functionality worked fine, until I (tried) to implement the new pdf file uploader. At first my Error looked pretty basic because it was clear, that the files I tried to upload where bigger than 16kb (Asp.Net Cores Uploadlimit), so I tried the "common" methods (s. below) to fix this error, that are found on Stack Overflow and similar sites:
services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = long.MaxValue;
x.MemoryBufferThreshold = 20;
});
or
public class RequestFormSizeLimitAttribute : Attribute, IAuthorizationFilter, IOrderedFilter
{
public int Order { get; }
private readonly FormOptions _formOptions;
public RequestFormSizeLimitAttribute(int valueCountLimit)
{
_formOptions = new FormOptions()
{
ValueCountLimit = valueCountLimit,
KeyLengthLimit = valueCountLimit
};
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var features = context.HttpContext.Features;
var formFeature = features.Get<IFormFeature>();
if (formFeature == null || formFeature.Form == null)
{
// Request form has not been read yet, so set the limits
features.Set(new FormFeature(context.HttpContext.Request, _formOptions));
}
}
}
or
<system.web>
<httpRuntime maxRequestLength="1048576" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>
however none of these Methods fixed my Error.
First of all my Jquery Code:
//For Image Uploading
function sendFiles(file) {
var data = new FormData();
data.append("file", file);
data.append('divisionId', $('#divisionId').val());
$.ajax({
data: data,
type: "POST",
url: "/api/File/UploadImageAjax",
cache: false,
contentType: false,
processData: false,
success: function (image) {
if (image !== "Error") {
var picture = $(image);
$('#summernote').summernote("insertNode", picture[0]);
} else {
bootbox.alert("Fehler beim hochladen des Bildes.");
}
}
});
}
//For PDF-File Upload
$('#btnUpload').on('click', function (e) {
var validation = validator.form();
e.preventDefault();
if (validation) {
var data = new FormData();
var text = document.getElementById('pdfText').value;
var fileSelect = document.getElementById('pdfFile');
data.append('linkName', text);
data.append('divisionId', $('#divisionId').val());
var files = fileSelect.files;
data.append('file', files[0]);
closeModal();
$.ajax({
data: data,
type: "POST",
url: "/api/File/UploadTextFileAjax",
cache: false,
contentType: false,
processData: false,
success: function (file) {
var atag = document.createElement('a');
atag.setAttribute('href', file.absoulutePath);
atag.setAttribute('id', file.fileId);
atag.innerHtml = file.linkText;
$('#summernote').summernote('insertNode', atag);
},
error: function(respons) {
bootbox.alert("Fehler beim Speichern der Text Datei.");
}
});
}
});
And finally my C# Code from the Controller:
[HttpPost]
[RequestFormSizeLimit(valueCountLimit: 2147483647)]
public async Task<IActionResult> UploadImageAjax()
{
//Exception thrown here.
var files = Request.Form.Files;
var request = Request.Form.FirstOrDefault(d => d.Key == "divisionId");
var divisionName = await GetDivisionNameAsync(request);
if (divisionName != null)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
if (files.Count == 1 && user != null)
{
for (int i = 0; i < 1; i++)
{
var file = files[i];
if (TestImageFileName(file))
{
var path = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.ToString().Trim('"');
var fileName = $#"\images\Sportarten\{divisionName}\{path}";
if (!await _imageHandler.IsImageInDbAsync(fileName))
{
path = _hosting.WebRootPath + fileName;
using (FileStream fs = System.IO.File.Create(path))
{
await file.CopyToAsync(fs);
await fs.FlushAsync();
}
var image = new ImageViewModel { FileName = file.FileName, AbsolutePath = fileName, AspNetUserId = user.Id, FullPath = path };
var imageId = await _imageHandler.AddImageAsync(image);
var imageNode = $#"<img id='{imageId}' src='{fileName}' class='img-responsive'/>";
return Json(imageNode);
}
var id = await _imageHandler.GetImageIdbyNameAsync(path);
var node = $#"<img id='{id}' src='{fileName}' class='img-responsive'/>";
return Json(node);
}
}
}
}
return BadRequest("Error");
}
[HttpPost]
public async Task<IActionResult> UploadTextFileAjax()
{
//Exception thrown here.
var files = Request.Form.Files;
var request = Request.Form.FirstOrDefault(d => d.Key == "divisionId");
var divisionName = await GetDivisionNameAsync(request);
var linkText = Request.Form.FirstOrDefault(l => l.Key == "linkName").Value.ToString();
if (linkText != null && divisionName != null)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
if (files.Count == 1 && user != null)
{
for (int i = 0; i < 1; i++)
{
var file = files[i];
if (TestTextFileName(file))
{
var path = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.ToString().Trim('"');
var fileName = $#"\files\Sportarten\{divisionName}\{path}";
if (await _file.IsFileInDb(fileName))
{
path = _hosting.WebRootPath + fileName;
using (FileStream fs = System.IO.File.Create(path))
{
await file.CopyToAsync(fs);
await fs.FlushAsync();
}
var textFile = new FileViewModel
{
AbsolutePath = fileName,
AspNetUserId = user.Id,
FileName = file.FileName,
FullPath = path
};
var fileId = await _file.AddFile(textFile);
return Ok(new {absolutePath = path, fileId = fileId, linkText = linkText});
}
var oldText = await _file.FindFilebyName(path);
return Ok(new { absolutePath = oldText.AbsolutePath, fileId = oldText.FileId, linkText = linkText });
}
}
}
}
return BadRequest("Error");
}
At last here is my Stacktrace I get, when the error is thrown(sorry for the bad quality):
Full Stacktrace of Error
I'm trying to send .csv file from my client app (angular 2) to my web api (ASP.NET), and I have done the following:
Tried to make FormData from my .csv file the following way:
public sendData() {
let formData = new FormData();
formData.append('file', this.file, this.file.name);
this.myService.postMyData(formData, this.name)
.subscribe(data => this.postData = JSON.stringify(data),
error => this.error = error,
() => console.log('Sent'));
}
Created a service on the client app where I'm sending this .csv file from.
postMyData(formData: any, name: string) {
this.s = <string><any>name;
const headers = new Headers();
headers.append('Content-Disposition', 'form-data');
const url: string = 'myUrl?methodName=' + name;
return this.http.post(url, formData, {headers: headers})
.map((res: Response) => res.json());
}
What's the problem now is that I don't know how to get that .csv file on the server. I tried it with the code found below, but I can't get the real content, I can only see the name, content type, length and stuff like that.
[HttpPost("GetMyCsvFile")]
public async Task<IActionResult> GetMyCsvFile(string name) {
var rawMessage = await Request.ReadFormAsync();
var msg = rawMessage.Files[0];
....
}
And then whatever I do with rawMessage, I can't get the content which I could read and do the stuff needed.
Is this possible to do?
You need to get the file and not the file name. Try this code, I'm getting a CSV file from my angular app.
public async Task<bool> GetFileFromAngular(IFormFile file) {
using (var reader = new StreamReader(file.OpenReadStream())) {
var config = new CsvConfiguration(CultureInfo.InvariantCulture) {
HasHeaderRecord = true,
MissingFieldFound = null,
BadDataFound = null,
TrimOptions = TrimOptions.Trim
};
using (var csv = new CsvReader(reader, config)) {
try {
var records = csv.GetRecords<DrugFormulary>().ToList();
var csvProcessor = new CsvProcessor(_dbContext, _configuration);
await csvProcessor.ProcessPlan(records);
} catch (System.Exception ex) {
throw ex;
}
}
}
return true;
}
I have to send the files along with some description about those to the server.
So as per above image, I want to upload a file and provide the description of file in a text box on right side of it. After clicking select files link, the user can select another file to upload and it will also have description text box. After clicking upload files, along with the file, description of it neet to upload to the server.
I am using plupload to do it. But it is just uploading file and not description.
Also, I am using MVC. So please suggest any solution to it or suggest any other javascript library which can fulfill my requirements.
Below is the MVC code,
public string Upload(List<HttpPostedFileBase> fileUploads,List<string> fileDescription)
{
int count = 0;
foreach (HttpPostedFileBase file in fileUploads)
{
byte[] fileData = new byte[file.ContentLength];
file.InputStream.Read(fileData, 0, file.ContentLength);
db.UploadedFiles.AddObject(new UploadedFile
{
FileDescription = fileDescription[count],
FileBinary = fileData,
FileName = file.FileName
});
count++;
}
db.SaveChanges();
return "Success";
}
Below is javascript code
var uploadFiles = [];
var descs = [];
var count = 0;
plupload.each(uploader.files, function (file) {
var id = file.id;
var fileUpload = file;
uploadFiles[count] = file;
descs[count] = $("#" + id + "_desc").val();
count++;
});
var da = { fileDescription: descs,fileUploads: uploader.files };
$.ajax({
url: '/LumosQC/Upload',
data: da,
method: 'POST',
}).done(function (data1) {
alert("Success");
}).error(function (a, b, c) {
console.log(a);
});
You can modify the route you use for uploading and use something like
...
[Route("upload/{description}")]
public HttpResponseMessage Upload(string description)
...
Or you can put description into cookie (but I would recomend to use the first approach it's cleaner)
function createCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
createCookie('desciption', 'your description', 1)
and then
Request.Cookies["description"]
UPDATE
Now I see you need to upload multiple files, for that you can use the same approach with modified route
[Route("upload")]
public string Upload(List<HttpPostedFileBase> fileUploads, [FromUri] string[] fileDescription)
Create view model and use as parameter in action method,
ViewModel :
public class UploadViewModel
{
public List<string> FileDescriptions;
public List<HttpPostedFileBase> Files;
}
Action method :
public string Upload(UploadViewModel model)
{
// ....
}
that will bind the data correctly.
Below I have code from my image upload returning JSON information about the image. Near the assignment of the name parameter I am trying to return the URL of the image although it is not working. Currently the below code does not work as nothing is returned to the browser upon return. The code works fine albeit for the line to add to the Name property near the end.
How can I return the URL of the image so as to display the image URL to the user on return?
[HttpPost]
public ContentResult UploadFiles()
{
var r = new List<UploadFilesResult>();
foreach (string file in Request.Files)
{
HttpPostedFileBase hpf = Request.Files[file] as HttpPostedFileBase;
if (hpf.ContentLength == 0)
continue;
string savedFileName = Url.Content(Path.Combine(Server.MapPath("~/Images"), Path.GetFileName(hpf.FileName)));
hpf.SaveAs(GetNewPathForDupes(savedFileName));
r.Add(new UploadFilesResult()
{
Name = Path.Combine(Server.MapPath("~/Images"), Path.GetFileName(hpf.FileName)),
Length = hpf.ContentLength,
Type = hpf.ContentType
});
}
return Content("{\"name\":\"" + r[0].Name + "\",\"type\":\"" + r[0].Type + "\",\"size\":\"" + string.Format("{0} bytes", r[0].Length) + "\"}", "application/json");
}
EDIT
my Javascript to process the form in the done parameter i am trying to read the JSON but cant get it to work with the new Jsonresult:
$(document).ready(function () {
$('#fileupload').fileupload({
dataType: 'json',
url: "UploadFiles",
autoUpload: true,
done: function (e, data) {
var json = JSON.parse(data);
$('.file_name').html(json.name);
$('.file_type').html(json.type);
$('.file_size').html(json.size);
}
}).on('fileuploadprogressall', function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('.progress .progress-bar').css('width', progress + '%');
});
});
So instead of
return this.Content("{\"name\":\"" + r[0].Name + ...
you could simply do this:
return this.Json(r[0]);
When reading the JSON in your jQuery you will need to do
var jsonString = // from the response
var json = $.parseJSON(jsonString);
var name = json.someElement;
I want to return a string from generic handler and get it into angularjs. My code is below.
Generic handler:
public void ProcessRequest(HttpContext context)
{
List<string> path= new List<string>();
if (context.Request.Files.Count > 0)
{
HttpFileCollection files = context.Request.Files;
for (int i = 0; i < files.Count; i++)
{
HttpPostedFile file = files[i];
string fname = context.Server.MapPath("~/upload/" + file.FileName);
//file.SaveAs(fname);
path.Add(fname);
}
}
string abc = path[0].ToString();
context.Response.ContentType = "text/plain";
context.Response.Write(abc);
}
Angularjs Controller:
$scope.uploadFile = function () {
var fd = new FormData()
for (var i in ScopGloble.files) {
// alert("hi I am in");
fd.append("uploadedFile", $scope.files[i])
}
var xhr = new XMLHttpRequest()
xhr.open("POST", "FileuploadTask.ashx")
ScopGloble.progressVisible = true
xhr.send(fd)
//How I can get file path here.
}
How I can get file path.
You want the XHR object's responseText.
However, you can't get it where you have the comment, since XMLHttpRequest is typically asynchronous. You'd need something like
//before we send the request
xhr.onload = function(xhrEventArgs) {
var ThePath = xhr.responseText;
//do something with that string
};
//now, send the request
xhr.send(fd);
You should probably check the request status in there too. See the MDN XHR documentation for more info on that.