Reputation: 743
I'm trying to upload a file asynchronously to my web server ( ASP.NET MVC). Specifically to my web api endpoint. I followed several tutorials but I keep getting an error of null parameters.
It happens when I subclass MultipartFormDataStreamProvider
. The method GetLocalFileName
, gets called first correctly, with the proper HttpContentHeaders
, but then gets called a second time with null
headers.
The file is successfully saved with the filename I set, but as I get an exception, I am not able to know what's the name generated.
However if I just use the base MultipartFormDataStreamProvider
class, the file is saved perfectly, with no errors, but I can't control the filename in this case.
This is the api method:
[HttpPost]
public async Task<FilesUploadResult> Save()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
var ret = new FilesUploadResult(HttpStatusCode.UnsupportedMediaType, null);
ret.Message = "Unsupported Media Type. Request must contain multipart/form-data form.";
return ret;
}
var path = UploadsFolderPath;
// to-do: this works but for some reason makes a call with empty headers.
var provider = new SimpleMultipartFormDataStreamProvider(path);
// this provider saves the file with an arbritrary name
//var provider = new MultipartFormDataStreamProvider(path);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider as MultipartFormDataStreamProvider);
var files = from f in provider.FileData
let info = new FileInfo(f.LocalFileName)
select new FileUploadDescription(info.Name, path + "\\" + info.Name, info.Length, HttpStatusCode.Created);
var ret = new FilesUploadResult(HttpStatusCode.OK, files);
return ret;
}
catch (Exception ex)
{
var ret = new FilesUploadResult(HttpStatusCode.InternalServerError, null);
ret.Message = ex.Message;
return ret;
}
}
This is the code for my SimpleMultipartFormDataStreamProvider
:
public class SimpleMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public SimpleMultipartFormDataStreamProvider(string path) : base(path) { }
public override string GetLocalFileName(HttpContentHeaders headers)
{
// make sure the headers are valid
if (headers == null)
{
throw new ArgumentNullException("headers");
}
// filename
var filename = CleanString(headers.ContentDisposition.FileName); ;
// create the local file name
var localFileName = string.Format("_{0}_{1}", GetRandomName(), filename);
//this is here because Chrome submits files in quotation marks which get treated as part of the filename and get escaped
return localFileName.Replace("\"", string.Empty);
}
private string GetRandomName()
{
return GuidHelper.ShortUniqueId(16);
}
private static string CleanString(string str)
{
if (string.IsNullOrWhiteSpace(str))
return string.Empty;
var sb = new StringBuilder();
foreach (char c in str)
{
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_')
{
sb.Append(c);
}
}
return sb.ToString();
}
}
Can anybody tell me why I am getting the GetLocalFileName
called twice and the 2nd time with empty headers?
Upvotes: 1
Views: 2910
Reputation: 743
I figured it out by myself.
The issue was that the class I implemented to subclass the MultipartDataStreamProvider
was in a different project.
That project had references to .Net .Http assemblies that were different to the ones currently in the Web Api project.
Only thing I did was to move that CustomMultipartDataStreamProvider class to the same project, and everything worked perfectly.
Upvotes: 2
Reputation: 1316
GetLocalFileName is called once for each file in the multipart request. If you you send two files in the same request GetLocalFileName will be invoked twice. I think you should check how are you invoking the service and use fiddler or something similar to see if you really are sending two files. If you send several parts in the same request try to specify the ContentType of each part.
Upvotes: 2
Reputation: 1305
Even i had encountered such behavior in MVC4 it submits 2 files one of which is empty, so better would be to have a check whether the file has any content and then proceed with saving the file.
There can be multiple approaches for this:
One of them would be to get the list of all the files from Request.Files
(this is an array of strings )
foreach (string fileName in Request.Files)
{
HttpPostedFileBase file = Request.Files[fileName];
//Save file content goes here
if (file != null && file.ContentLength > 0)
{
//logic to save the content
}
}
Upvotes: 2