带有额外参数的 Webapi 表单数据上传(到 DB) [英] Webapi formdata upload (to DB) with extra parameters

查看:27
本文介绍了带有额外参数的 Webapi 表单数据上传(到 DB)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to upload file sending extra paramaters.

我在 stackoverflow 中发现了以下帖子:Webapi ajax formdata upload with extra参数

I have found the following post in stackoverflow: Webapi ajax formdata upload with extra parameters

它描述了如何使用 MultipartFormDataStreamProvider 执行此操作并将数据保存到文件服务器.我不需要将文件保存到服务器,而是保存到数据库.我已经使用 MultipartMemoryStreamProvider 编写了代码,但它不使用额外的参数.

It describes how to do this using MultipartFormDataStreamProvider and saving data to fileserver. I do not need to save file to server, but to DB instead. And I have already working code using MultipartMemoryStreamProvider, but it doesn't use extra parameter.

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

Can you give me clues how to process extra paramaters in webapi?

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

For example, if I add file and also test paramater:

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

这是我的 webapi,它在没有额外参数的情况下处理文件上传:

Here is my webapi that processes fileupload without extra paramater:

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;
}

推荐答案

您可以通过实现一个自定义的 DataStreamProvider 以一种不太干净的方式实现这一点,该DataStreamProvider 复制了解析 FormData 的逻辑来自 MultipartFormDataStreamProvider 的多部分内容.

You can achieve this in a not-so-very-clean manner by implementing a custom DataStreamProvider that duplicates the logic for parsing FormData from multi-part content from MultipartFormDataStreamProvider.

我不太确定为什么决定将 MultipartFormDataStreamProviderMultiPartFileStreamProvider 子类化,而至少没有提取标识和公开 FormData 集合的代码,因为它很有用适用于许多涉及多部分数据的任务,而不仅仅是将文件保存到磁盘.

I'm not quite sure why the decision was made to subclass MultipartFormDataStreamProvider from MultiPartFileStreamProvider without at least extracting the code that identifies and exposes the FormData collection since it is useful for many tasks involving multi-part data outside of simply saving a file to disk.

无论如何,以下提供商应该可以帮助解决您的问题.您仍然需要确保在迭代提供程序内容时忽略任何没有文件名的内容(特别是语句 streamProvider.Contents.Select() 否则您可能会尝试将表单数据上传到数据库).因此,请求提供者的代码是一个 HttpContent IsStream(),这有点小技巧,但最简单的是我能想到的去做.

Anyway, the following provider should help solve your issue. You will still need to ensure that when you iterate the provider content you are ignoring anything that does not have a filename (specifically the statement streamProvider.Contents.Select() else you risk trying to upload the formdata to the DB). Hence the code that asks the provider is a HttpContent IsStream(), this is a bit of a hack but was the simplest was I could think to do it.

请注意,它基本上是来自 MultipartFormDataStreamProvider 来源的剪切和粘贴斧头作业 - 它尚未经过严格测试(灵感来自 这个答案).

Note that it is basically a cut and paste hatchet job from the source of MultipartFormDataStreamProvider - it has not been rigorously tested (inspired by this answer).

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];
    }
}

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

It can be used as follows (using TPL syntax to match your question):

[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;
    });
}

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

I tested it with the following HTML form:

<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>

这篇关于带有额外参数的 Webapi 表单数据上传(到 DB)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆