Return a string from generic handler - c#

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.

Related

Tracking HttpClient.PostAsync progress using Blazor and Aspnet Webapi

I am currently using Blazor and Asp.Net WebApi Controller to perform some file upload/download..And I have a UploadFiles() called in the Blazor client that looks like this:
public async Task<bool> UploadFiles(Action<int> callback, IReadOnlyList<IBrowserFile> files, string subdir = "")
{
if (files != null)
{
var content = new MultipartFormDataContent();
foreach (var file in files)
{
var fileContent = new StreamContent(file.OpenReadStream(_allowedMaxFileSize));
content.Add(fileContent, "\"files\"", file.Name);
hasItemsForUpload = true;
}
if (content != null)
{
var uri = $#"api/mycontroller/upload?subdir=" + subdir;
var result = await _http.PostAsync(uri, content);
if (result.IsSuccessStatusCode)
{
return true;
}
}
}
return false;
}
Is there a way for me to easily track httpclient.postasync() % progress where I can pass the % as int in my callback? Note that _http here is injected through dependency injection registered as AddScope() of the startup.cs
I've seen complicated examples using custom http content but I can't seem to wrap my head around it... :(
Please help..
FileComponent.razor
<div> Progress: #current </div>
#code
{
int current = 0;
void UpdateProgress()
{
current++;
StateHasChanged();
}
void ResetBatch(){ current=0; }
public async Task<bool> UploadFiles(Action<int> callback, IReadOnlyList<IBrowserFile> files, string subdir = "")
{
ResetBatch();
if (files != null)
{
var content = new MultipartFormDataContent();
foreach (var file in files)
{
var fileContent = new StreamContent(file.OpenReadStream(_allowedMaxFileSize));
content.Add(fileContent, "\"files\"", file.Name);
hasItemsForUpload = true;
if (content != null)
{
var uri = $#"api/mycontroller/upload?subdir=" + subdir;
var result = await _http.PostAsync(uri, content);
}
UpdateProgress();
}
}
ResetBatch();
return true;
}
}
Note that instead of 'current' you can create a whole upload batch class that contains information regarding total files to be updated, filenames, current file name, status of each upload etc etc.

How to get the extension from ContentDisposition [duplicate]

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

Accessing Request.Form.Files throws Exception System.io.InvalidDataException

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

result of call back function return to view from model asynchronously

i have one call back function in my model. i.e void method.
how to pass that result to view.
my problem is while uploading file to amazon s3, it's returning progress value. i need to get that value in view.
my code as follows
public bool sendMyFileToS3(EmployeeModel e, string bucketName, string subDirectoryInBucket)
{
IAmazonS3 client = new AmazonS3Client(RegionEndpoint.USEast2);
TransferUtility utility = new TransferUtility(client);
TransferUtilityUploadRequest request = new TransferUtilityUploadRequest();
if (subDirectoryInBucket == "" || subDirectoryInBucket == null)
{
request.BucketName = bucketName; //no subdirectory just bucket name
}
else
{ // subdirectory and bucket name
request.BucketName = bucketName + #"/" + subDirectoryInBucket;
}
try
{
request.Key = RandomString() + Path.GetExtension(e.File.FileName); //file name up in S3
request.InputStream = e.File.InputStream;
request.CannedACL = S3CannedACL.PublicRead;
request.UploadProgressEvent += new EventHandler<UploadProgressArgs>(UploadFile_ProgressBar); //call bcak function
utility.Upload(request);
}
catch(AmazonS3Exception)
{
throw;
}
//commensing the transfer
//Generate link with expiry date.
Amazon.S3.Model.GetPreSignedUrlRequest aa = new Amazon.S3.Model.GetPreSignedUrlRequest();
aa.BucketName = request.BucketName;
aa.Key = request.Key;
aa.Expires = new DateTime().AddDays(2);
string url = client.GetPreSignedURL(aa);
url = url.Remove(url.IndexOf('?'));
return true;
}
public void UploadFile_ProgressBar(object sender, UploadProgressArgs e)
{
int pctProgress = (int)(e.TransferredBytes * 100 / e.TotalBytes);
}
every second "pctProgress" this parameter is replacing with new value. when ever replacing with new value it should pass to view.
how to do this.
please any suggestions.

Upload data with file

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.

Categories

Resources