ReadAsMultipartAsync Throws System.ArgumentException - asp.net

I have tried searching on here and Google for an answer to this but have yet to find one. I am using what I have found to be fairly standard for .NET 4.0 upload to Web API service. Here is the code:
public HttpResponseMessage Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
StringBuilder sb = new StringBuilder();
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MyMultipartFormDataStreamProvider(root);
var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}
// This will give me the form field data
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
sb.Append(string.Format("{0}: {1}", key, val));
}
}
// This will give me any file upload data
foreach (MultipartFileData file in provider.FileData)
{
sb.Append(file.Headers.ContentDisposition.FileName);
sb.Append("Server file path: " + file.LocalFileName);
}
return new HttpResponseMessage()
{
Content = new StringContent(sb.ToString())
};
});
return Request.CreateResponse(HttpStatusCode.OK);
}
When I create a very basic form with a input type=file and submit it I am getting an exception thrown for files over about 800Kb. Here is the exception:
System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=Value does not fall within the expected range.
Source=mscorlib
StackTrace:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode)
at System.Web.Hosting.IIS7WorkerRequest.GetServerVariableInternal(String name)
at System.Web.Hosting.IIS7WorkerRequest.GetServerVariable(String name)
at System.Web.Hosting.IIS7WorkerRequest.GetRemoteAddress()
at System.Web.HttpWorkerRequest.IsLocal()
at System.Web.Configuration.CustomErrorsSection.CustomErrorsEnabled(HttpRequest request)
at System.Web.HttpContextWrapper.get_IsCustomErrorEnabled()
at System.Web.Http.WebHost.HttpControllerHandler.<>c__DisplayClassa.<ConvertRequest>b__9()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at System.Lazy`1.get_Value()
at System.Web.Http.HttpConfiguration.ShouldIncludeErrorDetail(HttpRequestMessage request)
at System.Net.Http.HttpRequestMessageExtensions.CreateErrorResponse(HttpRequestMessage request, HttpStatusCode statusCode, Func`2 errorCreator)
at System.Net.Http.HttpRequestMessageExtensions.CreateErrorResponse(HttpRequestMessage request, HttpStatusCode statusCode, Exception exception)
at aocform.Controllers.ValuesController.<>c__DisplayClass2.<Post>b__1(Task`1 t) in c:\Users\fred_malone\Documents\Visual Studio 2012\Projects\aocform\aocform\Controllers\ValuesController.cs:line 30
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
InnerException:
I check the App_Data folder and I see part of the file there. This small part is not always the same size either, like maybe it cuts off at a certain size.
I have adjusted both the maxRequestLength and the maxAllowedContentLength to large numbers with no success.
What does this message mean and what should I be looking at to fix it?
Thanks.

Related

Return PDF to the Browser using ASP.NET Core

I created the Wep API in ASP.Net core to return the PDF. Here is my code:
public HttpResponseMessage Get(int id)
{
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
var stream = new System.IO.FileStream(#"C:\Users\shoba_eswar\Documents\REquest.pdf", System.IO.FileMode.Open);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "NewTab";
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
return response;
}
But it returns only the JSON response:
{
"version":{
"major":1,
"minor":1,
"build":-1,
"revision":-1,
"majorRevision":-1,
"minorRevision":-1
},
"content":{
"headers":[
{
"key":"Content-Disposition",
"value":[
"attachment; filename=NewTab"
]
},
{
"key":"Content-Type",
"value":[
"application/pdf"
]
}
]
},
"statusCode":200,
"reasonPhrase":"OK",
"headers":[
],
"requestMessage":null,
"isSuccessStatusCode":true
}
Am I doing anything wrong here?
As explained in ASP.NET Core HTTPRequestMessage returns strange JSON message, ASP.NET Core does not support returning an HttpResponseMessage (what package did you install to get access to that type?).
Because of this, the serializer is simply writing all public properties of the HttpResponseMessage to the output, as it would with any other unsupported response type.
To support custom responses, you must return an IActionResult-implementing type. There's plenty of those. In your case, I'd look into the FileStreamResult:
public IActionResult Get(int id)
{
var stream = new FileStream(#"path\to\file", FileMode.Open);
return new FileStreamResult(stream, "application/pdf");
}
Or simply use a PhysicalFileResult, where the stream is handled for you:
public IActionResult Get(int id)
{
return new PhysicalFileResult(#"path\to\file", "application/pdf");
}
Of course all of this can be simplified using helper methods, such as Controller.File():
public IActionResult Get(int id)
{
var stream = new FileStream(#"path\to\file", FileMode.Open);
return File(stream, "application/pdf", "FileDownloadName.ext");
}
This simply abstracts the creation of a FileContentResult or FileStreamResult (for this overload, the latter).
Or if you're converting an older MVC or Web API application and don't want to convert all your code at once, add a reference to WebApiCompatShim (NuGet) and wrap your current code in a ResponseMessageResult:
public IActionResult Get(int id)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = ...
response.Content...
return new ResponseMessageResult(response);
}
If you don't want to use return File(fileName, contentType, fileDownloadName), then the FileStreamResult doesn't support setting the content-disposition header from the constructor or through properties.
In that case you'll have to add that response header to the response yourself before returning the file result:
var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName("foo.txt");
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
I couldn't comment the answer by CodeCaster since my reputation isn't high enough.
When trying
public IActionResult Get(int id)
{
using (var stream = new FileStream(#"path\to\file", FileMode.Open))
{
return File(stream, "application/pdf", "FileDownloadName.ext");
}
}
we got a
ObjectDisposedException: Cannot access a disposed object. Object name:
'Cannot access a closed file.'. System.IO.FileStream.BeginRead(byte[]
array, int offset, int numBytes, AsyncCallback callback, object state)
We removed the using
[HttpGet]
[Route("getImageFile")]
public IActionResult GetWorkbook()
{
var stream = new FileStream(#"pathToFile", FileMode.Open);
return File(stream, "image/png", "image.png");
}
And that worked. This is ASP.NET Core 2.1 running in IIS Express.
I don't have enough reputation to post this as a comment, so posting as an answer. The first 3 solutions from #CodeCaster and the solution from #BernhardMaertl are correct.
However, for someone who may not work with files often (like me), please note that if the process running this code (e.g. the API) only has read permissions to the file, you will need to specify that as the third parameter when creating your FileStream, otherwise the default behavior is to open the file for read/write and you will get an exception since you do not have write permissions.
The 3rd solution from #CodeCaster would then look like this:
public IActionResult Get(int id)
{
var stream = new FileStream(#"path\to\file", FileMode.Open, FileAccess.Read);
return File(stream, "application/pdf", "FileDownloadName.ext");
}

ASP.NET Core RC-1 file upload

I am currently uploading a file via the kendo fileuploader to an api controller using ASP.NET core RC-1. I am receiving a periodic error of "object reference not set to instance of object" when attempting to read the stream following opening the stream with IFormFile.OpenReadStream().
My controller is:
[HttpPost]
[Route("api/{domain}/[controller]")]
public async Task<IActionResult> Post([FromRoute]string domain, [FromForm]IFormFile file, [FromForm]WebDocument document)
{
if (ModelState.IsValid)
{
if (file.Length > 0)
{
var userName =
Request.HttpContext.User.Claims
.FirstOrDefault(c => c.Type == ClaimTypesEx.FullName)?
.Value;
var uploadedFileName =
ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
document.Domain = domain;
document.MimeType = file.ContentType;
document.SizeInBytes = file.Length;
document.ChangedBy = userName;
document.FileName = (string.IsNullOrEmpty(document.FileName)) ? uploadedFileName : document.FileName;
try
{
document = await CommandStack.For<WebDocument>()
.AddOrUpdateAsync(document, file.OpenReadStream()).ConfigureAwait(false);
}
catch (Exception e)
{
return new HttpStatusCodeResult(500);
}
return Ok(document);
}
}
return new BadRequestResult();
}
And the error is being thrown when I actually try to read the stream when it is going into blob storage:
public async Task<Uri> CreateOrUpdateBlobAsync(string containerName, string fileName, string mimeType,
Stream fileStream)
{
var container = Client.GetContainerReference(containerName);
var blob = container.GetBlockBlobReference(fileName);
//Error HERE
await blob.UploadFromStreamAsync(fileStream);
blob.Properties.ContentType = mimeType;
await blob.SetPropertiesAsync();
return blob.Uri;
}
What I am having trouble with is this is sporadic and there seems to be no defined pattern of which files are accepted and which ones generate the error. At first I thought it might be a size issue but that is not the case as I have several larger files uploaded successfully and then one small file will throw the error. Images seem to work fine and it is hit or miss on other file types with no rhyme or reason that I can figure out.

download file from google drive using asp.net

I am trying download file from google drive using the code below:
public static Boolean downloadFile(string downloadurl, string _saveTo)
{
if (!String.IsNullOrEmpty(downloadurl))
{
try
{
var x = service.HttpClient.GetByteArrayAsync(downloadurl);
byte[] arrBytes = x.Result;
System.IO.File.WriteAllBytes(_saveTo, arrBytes);
return true;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
return false;
}
}
else
{
// The file doesn't have any content stored on Drive.
return false;
}
}
On debugging the above code throwing exception as below:
?service.HttpClient.GetByteArrayAsync(downloadurl)
Id = 10, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
AsyncState: null
CancellationPending: false
CreationOptions: None
Exception: null
Id: 10
Result: null
Status: WaitingForActivation
I am trying to do it from my Service account created using Google API Console.
and the exception detail is as follows:
System.NullReferenceException was caught
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=System.Net.Http
StackTrace:
at System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(HttpHeaders sourceHeaders)
at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)
at System.Net.Http.HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.GetAsync(Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.GetContentAsync[T](Uri requestUri, HttpCompletionOption completionOption, T defaultValue, Func`2 readAs)
at System.Net.Http.HttpClient.GetByteArrayAsync(Uri requestUri)
at System.Net.Http.HttpClient.GetByteArrayAsync(String requestUri)
You can try this.
link
using Google.Apis.Authentication;
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
using System.Net;
public class MyClass {
public static System.IO.Stream DownloadFile(
IAuthenticator authenticator, File file) {
if (!String.IsNullOrEmpty(file.DownloadUrl)) {
try {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
new Uri(file.DownloadUrl));
authenticator.ApplyAuthenticationToRequest(request);
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK) {
return response.GetResponseStream();
} else {
Console.WriteLine(
"An error occurred: " + response.StatusDescription);
return null;
}
} catch (Exception e) {
Console.WriteLine("An error occurred: " + e.Message);
return null;
}
} else {
// The file doesn't have any content stored on Drive.
return null;
}
}
}
Code using the Google .net client library
Service account:
string[] scopes = new string[] {DriveService.Scope.Drive}; // Full access
var keyFilePath = #"c:\file.p12" ; // Downloaded from https://console.developers.google.com
var serviceAccountEmail = "xx#developer.gserviceaccount.com"; // found https://console.developers.google.com
//loading the Key file
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential( new ServiceAccountCredential.Initializer(serviceAccountEmail) {
Scopes = scopes}.FromCertificate(certificate));
create drive service
var service = new DriveService(new BaseClientService.Initializer() {HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",});
You can use files.list to list all of the files on drive.
FilesResource.ListRequest request = service.Files.List();
request.Q = "trashed=false";
title = 'hello'
FileList files = request.Execute();
loop though the items returned find the file you want it is a file resource you can pass it to the following method to down load your file
/// <summary>
/// Download a file
/// Documentation: https://developers.google.com/drive/v2/reference/files/get
/// </summary>
/// <param name="_service">a Valid authenticated DriveService</param>
/// <param name="_fileResource">File resource of the file to download</param>
/// <param name="_saveTo">location of where to save the file including the file name to save it as.</param>
/// <returns></returns>
public static Boolean downloadFile(DriveService _service, File _fileResource, string _saveTo)
{
if (!String.IsNullOrEmpty(_fileResource.DownloadUrl))
{
try
{
var x = _service.HttpClient.GetByteArrayAsync(_fileResource.DownloadUrl );
byte[] arrBytes = x.Result;
System.IO.File.WriteAllBytes(_saveTo, arrBytes);
return true;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
return false;
}
}
else
{
// The file doesn't have any content stored on Drive.
return false;
}
}
code ripped from Google drive authentication C#

File Upload : ApiController

I have a file being uploaded using http post request using multipart/form-data to my class that is extending from ApiController.
In a dummy project, I am able to use:
HttpPostedFileBase hpf = Request.Files[file] as HttpPostedFileBase
to get the file inside my controller method where my Request is of type System.Web.HttpRequestWrapper.
But inside another production app where I have constraints of not adding any libraries/dlls, I don't see anything inside System.Web.HttpRequestWrapper.
My simple requirement is to get the posted file and convert it to a byte array to be able to store that into a database.
Any thoughts?
This code sample is from a ASP.NET Web API project I did sometime ago. It allowed uploading of an image file. I removed parts that were not relevant to your question.
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
var provider = await Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider());
var firstImage = provider.Contents.FirstOrDefault();
if (firstImage == null || firstImage.Headers.ContentDisposition.FileName == null)
return Request.CreateResponse(HttpStatusCode.BadRequest);
using (var ms = new MemoryStream())
{
await firstImage.CopyToAsync(ms);
var byteArray = ms.ToArray();
}
return Request.CreateResponse(HttpStatusCode.Created);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}

Why is ASP.NET View Engine checking for .Mobile.chstml view path?

For my ASP.NET MVC 4 project, I'm trying to implement a custom view engine to find an "Index.cshtml" view file if one exists within a folder. Additionally, I'm throwing a 404 for all view paths that are not found.
The 404 works when a view file doesn't exist. When a view file does exist, the view engine will then try looking for a .Mobile.cshtml file using the FileExists() function. There is no .mobile.cshtml file, so it throws an exception. Why does the view engine still look for a .mobile.cshtml file when it has found the non-mobile file already?
For example, when the view engine is able to find a view path at "~/Views/About/History/Index.cshtml", it will then try finding the file "~/Views/About/History/Index.Mobile.cshtml". Below is my full code for the custom view engine.
namespace System.Web.Mvc
{
// Extend where RazorViewEngine looks for view files.
// This looks for path/index.ext file if no path.ext file is found
// Ex: looks for "about/history/index.chstml" if "about/history.cshtml" is not found.
public class CustomViewEngine : RazorViewEngine
{
public BeckmanViewEngine()
{
AreaViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}/Index.cshtml",
};
ViewLocationFormats = new[]
{
"~/Views/{1}/{0}/Index.cshtml",
};
}
// Return 404 Exception if viewpath file in existing path is not found
protected override bool FileExists(ControllerContext context, string path)
{
if (!base.FileExists(context, path))
{
throw new HttpException(404, "HTTP/1.1 404 Not Found");
}
return true;
}
}
}
I have found the answer after digging a bit in the MVC 4 source code.
The RazorViewEngine derives from BuildManagerViewEngine, and this one in turns derives from VirtualPathProviderViewEngine.
It is VirtualPathProviderViewEngine the one that implements the method FindView:
public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (String.IsNullOrEmpty(viewName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
}
string[] viewLocationsSearched;
string[] masterLocationsSearched;
string controllerName = controllerContext.RouteData.GetRequiredString("controller");
string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched);
string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched);
if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
}
return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
}
That GetPath method used there will do something like this when the view path has not been cached yet:
return nameRepresentsPath
? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations)
: GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);
Getting there! The interesting method is GetPathFromGeneralName, which is the one trying to build the whole path for the view and checking if that path exists. The method is looping through each of the view locations that were registered in the View Engine, updating the view path with the display mode valid for current HttpContext and then checking if the resolved path exists. If so, the view has been found, is assigned to the result, cached and the result path returned.
private string GetPathFromGeneralName(ControllerContext controllerContext, List<ViewLocation> locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations)
{
string result = String.Empty;
searchedLocations = new string[locations.Count];
for (int i = 0; i < locations.Count; i++)
{
ViewLocation location = locations[i];
string virtualPath = location.Format(name, controllerName, areaName);
DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode);
if (virtualPathDisplayInfo != null)
{
string resolvedVirtualPath = virtualPathDisplayInfo.FilePath;
searchedLocations = _emptyLocations;
result = resolvedVirtualPath;
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, virtualPathDisplayInfo.DisplayMode.DisplayModeId), result);
if (controllerContext.DisplayMode == null)
{
controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode;
}
// Populate the cache for all other display modes. We want to cache both file system hits and misses so that we can distinguish
// in future requests whether a file's status was evicted from the cache (null value) or if the file doesn't exist (empty string).
IEnumerable<IDisplayMode> allDisplayModes = DisplayModeProvider.Modes;
foreach (IDisplayMode displayMode in allDisplayModes)
{
if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId)
{
DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path));
string cacheValue = String.Empty;
if (displayInfoToCache != null && displayInfoToCache.FilePath != null)
{
cacheValue = displayInfoToCache.FilePath;
}
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId), cacheValue);
}
}
break;
}
searchedLocations[i] = virtualPath;
}
return result;
}
You may have noticed that I havenĀ“t talked about a piece of code with the following comment (reformatted for clarity):
// Populate the cache for all other display modes.
// We want to cache both file system hits and misses so that we can distinguish
// in future requests whether a file's status was evicted from the cache
// (null value) or if the file doesn't exist (empty string).
That (and the piece of code below the comment :)) means that once MVC 4 has found the first valid path from the View Locations registered in the View Engine, it will also check if the view file for all of the additional display modes that were not tested exist, so that information can be included in the cache (although just for that view location and not all of the locations available in the view engine).
Notice also, how it is passing a lambda to each of the tested display modes for checking if the file for that mode exists:
DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(
controllerContext.HttpContext,
virtualPath,
virtualPathExists: path => FileExists(controllerContext, path));
So that explains why when you override FileExists it is also being called for the mobile view, even when it has already found the non-mobile view.
In any case, display modes can be removed the same way they can be added: by updating the DisplayModes collection when the application starts. For example, removing the Mobile display mode and leaving just the default and unspecific one (You cannot clear the collection or no view will ever be found):
...
using System.Web.WebPages;
...
protected void Application_Start()
{
DisplayModeProvider.Instance.Modes.Remove(
DisplayModeProvider.Instance.Modes
.Single(m => m.DisplayModeId == "Mobile"));
Quite a long answer but hopefully it makes sense!
Have you tried removing Mobile DisplayModeProvider. You can achieve this by running the following in Application_Start:
var mobileDisplayMode = DisplayModeProvider.Instance.Modes.FirstOrDefault(a => a.DisplayModeId == "Mobile");
if (mobileDisplayMode != null)
{
DisplayModeProvider.Instance.Modes.Remove(mobileDisplayMode);
}
THe problem that you are getting is an expected behavior because FindView method queries DisplayModeProvider.

Resources