29

追加のパラメーターを送信するファイルをアップロードする必要があります。

stackoverflow で次の投稿を見つけました: Webapi ajax formdata upload with extra parameters

MultipartFormDataStreamProvider を使用してデータをファイル サーバーに保存する方法について説明します。ファイルをサーバーに保存する必要はありませんが、代わりにDBに保存します。また、MultipartMemoryStreamProvider を使用して既にコードを実行していますが、追加のパラメーターは使用していません。

webapi で余分なパラメーターを処理する方法を教えてください。

たとえば、ファイルを追加してパラメーターもテストすると、次のようになります。

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

追加のパラメーターなしで fileupload を処理する私の webapi は次のとおりです。

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

4 に答える 4

31

DataStreamProviderのマルチパート コンテンツから FormData を解析するためのロジックを複製するカスタムを実装することにより、それほどクリーンではない方法でこれを実現できますMultipartFormDataStreamProvider

ファイルをディスクに保存するだけでなく、マルチパート データを含む多くのタスクに役立つため、少なくとも FormData コレクションを識別して公開するコードを抽出せずに、MultipartFormDataStreamProviderからサブクラス化する決定が下された理由はよくわかりません。MultiPartFileStreamProvider

とにかく、次のプロバイダーが問題の解決に役立つはずです. プロバイダーのコンテンツを反復するときに、ファイル名を持たないものはすべて無視していることを確認する必要があります (具体的にはstreamProvider.Contents.Select()、フォームデータを DB にアップロードしようとするリスクがある else ステートメント)。したがって、プロバイダーに要求するコードは HttpContent IsStream() です。これはちょっとしたハックですが、私が考えることができる最も簡単な方法でした。

これは基本的にソースからのカットアンドペーストの手斧の仕事であることに注意してください-厳密にテストされていません(この回答にMultipartFormDataStreamProvider触発されました)。

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構文を使用して質問に一致させます):

[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 フォームでテストしました。

<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>
于 2013-11-01T14:17:20.317 に答える
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();
}
于 2015-12-11T19:14:17.233 に答える