Webapi formdata上传(到DB)有额外的参数

时间:2013-11-01 08:14:48

标签: c# file-upload asp.net-web-api form-data

我需要上传文件发送额外的参数。

我在stackoverflow中找到了以下帖子:Webapi ajax formdata upload with extra parameters

它描述了如何使用MultipartFormDataStreamProvider并将数据保存到文件服务器。我不需要将文件保存到服务器,而是保存到DB。 我已经使用MultipartMemoryStreamProvider处理代码,但它不使用额外的参数。

你能否告诉我如何在webapi中处理额外的参数?

例如,如果我添加文件并测试参数:

data.append("myParameter", "test"); 

这是我的webapi处理fileupload而没有额外的参数:

if (Request.Content.IsMimeMultipartContent())
{               
    var streamProvider = new MultipartMemoryStreamProvider();
    var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable<FileModel>>(t =>
    {
        if (t.IsFaulted || t.IsCanceled)
        {
            throw new HttpResponseException(HttpStatusCode.InternalServerError);
        }

        _fleDataService = new FileDataBLL();
        FileData fle;

        var fleInfo = streamProvider.Contents.Select(i => {         
            fle = new FileData();
            fle.FileName = i.Headers.ContentDisposition.FileName;

            var contentTest = i.ReadAsByteArrayAsync();
            contentTest.Wait();
            if (contentTest.Result != null)
            {
                fle.FileContent = contentTest.Result;
            }                       

            // get extra parameters here ??????

            _fleDataService.Save(fle);

            return new FileModel(i.Headers.ContentDisposition.FileName, 1024); //todo
        });
        return fleInfo;
    });
    return task;
}

4 个答案:

答案 0 :(得分:33)

扩展gooid的答案,我将FormData提取封装到提供程序中,因为我遇到引用它的问题。在我看来,这只是提供了一个更好的实现。

public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider
{
    private readonly Collection<bool> _isFormData = new Collection<bool>();
    private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
    private readonly Dictionary<string, Stream> _fileStreams = new Dictionary<string, Stream>();

    public NameValueCollection FormData
    {
        get { return _formData; }
    }

    public Dictionary<string, Stream> FileStreams
    {
        get { return _fileStreams; }
    }

    public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
    {
        if (parent == null)
        {
            throw new ArgumentNullException("parent");
        }

        if (headers == null)
        {
            throw new ArgumentNullException("headers");
        }

        var contentDisposition = headers.ContentDisposition;
        if (contentDisposition == null)
        {
            throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part.");
        }

        _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
        return base.GetStream(parent, headers);
    }

    public override async Task ExecutePostProcessingAsync()
    {
        for (var index = 0; index < Contents.Count; index++)
        {
            HttpContent formContent = Contents[index];
            if (_isFormData[index])
            {
                // Field
                string formFieldName = UnquoteToken(formContent.Headers.ContentDisposition.Name) ?? string.Empty;
                string formFieldValue = await formContent.ReadAsStringAsync();
                FormData.Add(formFieldName, formFieldValue);
            } 
            else
            {
                // File
                string fileName = UnquoteToken(formContent.Headers.ContentDisposition.FileName);
                Stream stream = await formContent.ReadAsStreamAsync();
                FileStreams.Add(fileName, stream);
            }
        }
    }

    private static string UnquoteToken(string token)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            return token;
        }

        if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
        {
            return token.Substring(1, token.Length - 2);
        }

        return token;
    }
}

以下是我如何使用它。请注意,我使用等待,因为我们在.NET 4.5上。

    [HttpPost]
    public async Task<HttpResponseMessage> Upload()
    {
        if (!Request.Content.IsMimeMultipartContent())
        {
            return Request.CreateResponse(HttpStatusCode.UnsupportedMediaType, "Unsupported media type.");
        }

        // Read the file and form data.
        MultipartFormDataMemoryStreamProvider provider = new MultipartFormDataMemoryStreamProvider();
        await Request.Content.ReadAsMultipartAsync(provider);

        // Extract the fields from the form data.
        string description = provider.FormData["description"];
        int uploadType;
        if (!Int32.TryParse(provider.FormData["uploadType"], out uploadType))
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest, "Upload Type is invalid.");
        }

        // Check if files are on the request.
        if (!provider.FileStreams.Any())
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest, "No file uploaded.");
        }

        IList<string> uploadedFiles = new List<string>();
        foreach (KeyValuePair<string, Stream> file in provider.FileStreams)
        {
            string fileName = file.Key;
            Stream stream = file.Value;

            // Do something with the uploaded file
            UploadManager.Upload(stream, fileName, uploadType, description);

            // Keep track of the filename for the response
            uploadedFiles.Add(fileName);
        }

        return Request.CreateResponse(HttpStatusCode.OK, "Successfully Uploaded: " + string.Join(", ", uploadedFiles));
    }

答案 1 :(得分:29)

您可以通过实现自定义DataStreamProvider以非常非常干净的方式实现此目的,该自定义MultipartFormDataStreamProvider复制用于从MultipartFormDataStreamProvider的多部分内容解析FormData的逻辑。

我不太确定为什么决定从MultiPartFileStreamProvider继承streamProvider.Contents.Select()而不至少提取标识和公开FormData集合的代码,因为它对于涉及多个任务的许多任务很有用将数据保存到磁盘之外的部分数据。

无论如何,以下提供商应该有助于解决您的问题。您仍然需要确保在迭代提供程序内容时忽略任何没有文件名的内容(特别是语句MultipartFormDataStreamProvider,否则您可能会尝试将formdata上传到数据库)。因此,询问提供者的代码是一个HttpContent IsStream(),这有点像黑客,但最简单的是我能想到这样做。

请注意,它基本上是来自public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider { private readonly Collection<bool> _isFormData = new Collection<bool>(); private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase); public NameValueCollection FormData { get { return _formData; } } public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (parent == null) throw new ArgumentNullException("parent"); if (headers == null) throw new ArgumentNullException("headers"); var contentDisposition = headers.ContentDisposition; if (contentDisposition != null) { _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName)); return base.GetStream(parent, headers); } throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part."); } public override async Task ExecutePostProcessingAsync() { for (var index = 0; index < Contents.Count; index++) { if (IsStream(index)) continue; var formContent = Contents[index]; var contentDisposition = formContent.Headers.ContentDisposition; var formFieldName = UnquoteToken(contentDisposition.Name) ?? string.Empty; var formFieldValue = await formContent.ReadAsStringAsync(); FormData.Add(formFieldName, formFieldValue); } } private static string UnquoteToken(string token) { if (string.IsNullOrWhiteSpace(token)) return token; if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1) return token.Substring(1, token.Length - 2); return token; } public bool IsStream(int idx) { return !_isFormData[idx]; } } 来源的剪切和粘贴斧头作业 - 它尚未经过严格测试(灵感来自this answer)。

[HttpPost]
public Task<string> Post()
{
    if (!Request.Content.IsMimeMultipartContent())
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));

    var provider = new MultipartFormDataMemoryStreamProvider();

    return Request.Content.ReadAsMultipartAsync(provider).ContinueWith(p =>
    {
        var result = p.Result;
        var myParameter = result.FormData.GetValues("myParameter").FirstOrDefault();

        foreach (var stream in result.Contents.Where((content, idx) => result.IsStream(idx)))
        {
            var file = new FileData(stream.Headers.ContentDisposition.FileName);
            var contentTest = stream.ReadAsByteArrayAsync();
            // ... and so on, as per your original code.

        }
        return myParameter;
    });
}

它可以如下使用(使用TPL语法来匹配您的问题):

<form action="/api/values" method="post" enctype="multipart/form-data">
    <input name="myParameter" type="hidden" value="i dont do anything interesting"/>
    <input type="file" name="file1" />
    <input type="file" name="file2" />
    <input type="submit" value="OK" />
</form>

我使用以下HTML表单对其进行了测试:

{{1}}

答案 2 :(得分:3)

我真的需要上传文件的媒体类型和长度,所以我修改了@Mark Seefeldt对以下内容的回答:

public class MultipartFormFile
{
    public string Name { get; set; }
    public long? Length { get; set; }
    public string MediaType { get; set; }
    public Stream Stream { get; set; }
}

public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider
{
    private readonly Collection<bool> _isFormData = new Collection<bool>();
    private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
    private readonly List<MultipartFormFile> _fileStreams = new List<MultipartFormFile>();

    public NameValueCollection FormData
    {
        get { return _formData; }
    }

    public List<MultipartFormFile> FileStreams
    {
        get { return _fileStreams; }
    }

    public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
    {
        if (parent == null)
        {
            throw new ArgumentNullException("parent");
        }

        if (headers == null)
        {
            throw new ArgumentNullException("headers");
        }

        var contentDisposition = headers.ContentDisposition;
        if (contentDisposition == null)
        {
            throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part.");
        }

        _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
        return base.GetStream(parent, headers);
    }

    public override async Task ExecutePostProcessingAsync()
    {
        for (var index = 0; index < Contents.Count; index++)
        {
            HttpContent formContent = Contents[index];
            if (_isFormData[index])
            {
                // Field
                string formFieldName = UnquoteToken(formContent.Headers.ContentDisposition.Name) ?? string.Empty;
                string formFieldValue = await formContent.ReadAsStringAsync();
                FormData.Add(formFieldName, formFieldValue);
            }
            else
            {
                // File
                var file = new MultipartFormFile
                {
                    Name = UnquoteToken(formContent.Headers.ContentDisposition.FileName),
                    Length = formContent.Headers.ContentLength,
                    MediaType = formContent.Headers.ContentType.MediaType,
                    Stream = await formContent.ReadAsStreamAsync()
                };

                FileStreams.Add(file);
            }
        }
    }

    private static string UnquoteToken(string token)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            return token;
        }

        if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
        {
            return token.Substring(1, token.Length - 2);
        }

        return token;
    }
}

答案 3 :(得分:1)

最终,以下内容对我有用:

string root = HttpContext.Current.Server.MapPath("~/App_Data");

var provider = new MultipartFormDataStreamProvider(root);

var filesReadToProvider = await Request.Content.ReadAsMultipartAsync(provider);

foreach (var file in provider.FileData)
{
    var fileName = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
    byte[] documentData;

    documentData = File.ReadAllBytes(file.LocalFileName);

    DAL.Document newRecord = new DAL.Document
    {
        PathologyRequestId = PathologyRequestId,
        FileName = fileName,
        DocumentData = documentData,
        CreatedById = ApplicationSecurityDirector.CurrentUserGuid,
        CreatedDate = DateTime.Now,
        UpdatedById = ApplicationSecurityDirector.CurrentUserGuid,
        UpdatedDate = DateTime.Now
    };

    context.Documents.Add(newRecord);

    context.SaveChanges();
}