web-dev-qa-db-ja.com

ファイルのアップロードと追加の引数を取るWeb APIメソッド

ファイルをアップロードし、ファイルとともに追加情報を送信します。たとえば、文字列fooとint barを送信します。

ファイルアップロード、文字列、およびintを受け取るASP.NET WebAPIコントローラーメソッドをどのように記述しますか?

私のJavaScript:

var fileInput = document.querySelector("#filePicker");
var formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("foo", "hello world!");
formData.append("bar", 42);

var options = {
   url: "/api/foo/upload",
   data: formData,
   processData: false // Prevents JQuery from transforming the data into a query string
};
$.ajax(options);

私のWebAPIコントローラーは、次のようにファイルにアクセスできます。

public async Task<HttpResponseMessage> Upload()
{
    var streamProvider = new MultipartMemoryStreamProvider();
    await Request.Content.ReadAsMultipartAsync(streamProvider);
    var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
}

しかし、文字列とintをどのように取得できるかは明確ではありません。おそらくstreamProvider.Content [1]などと言うことができると思いますが、それは非常に厄介です。

ファイルのアップロード、文字列、およびintを受け入れるWebAPIアクションを記述する正しい方法©とは何ですか?

17

独自のMultipartFileStreamProviderを作成して、追加の引数にアクセスできます。

ExecutePostProcessingAsyncでは、各ファイルをマルチパート形式でループし、カスタムデータをロードします(ファイルが1つしかない場合は、CustomDataリストにオブジェクトが1つだけあります)。

class MyCustomData
{
    public int Foo { get; set; }
    public string Bar { get; set; }
}

class CustomMultipartFileStreamProvider : MultipartMemoryStreamProvider
{
    public List<MyCustomData> CustomData { get; set; }

    public CustomMultipartFileStreamProvider()
    {
        CustomData = new List<MyCustomData>();
    }

    public override Task ExecutePostProcessingAsync()
    {
        foreach (var file in Contents)
        {
            var parameters = file.Headers.ContentDisposition.Parameters;
            var data = new MyCustomData
            {
                Foo = int.Parse(GetNameHeaderValue(parameters, "Foo")),
                Bar = GetNameHeaderValue(parameters, "Bar"),
            };

            CustomData.Add(data);
        }

        return base.ExecutePostProcessingAsync();
    }

    private static string GetNameHeaderValue(ICollection<NameValueHeaderValue> headerValues, string name)
    {
        var nameValueHeader = headerValues.FirstOrDefault(
            x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));

        return nameValueHeader != null ? nameValueHeader.Value : null;
    }
}

次に、コントローラーで:

class UploadController : ApiController
{
    public async Task<HttpResponseMessage> Upload()
    {
        var streamProvider = new CustomMultipartFileStreamProvider();
        await Request.Content.ReadAsMultipartAsync(streamProvider);

        var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
        var customData = streamProvider.CustomData;

        return Request.CreateResponse(HttpStatusCode.Created);
    }
}
19
Ben Foster

ここでの答えは素晴らしいと思います。そのため、要約形式のファイルに加えてデータを渡す方法のやや単純な例を見ることができます。FileUploadコントローラーへのWebAPI呼び出しを行うJavascript関数、およびFileUploadコントローラーからのスニペット(VB.net)が含まれますJavaScriptから渡された追加データを読み取ります。

Javascript:

            function uploadImage(files) {
            var data = new FormData();
            if (files.length > 0) {
                data.append("UploadedImage", files[0]);
                data.append("Source", "1")
                var ajaxRequest = $.ajax({
                    type: "POST",
                    url: "/api/fileupload/uploadfile",
                    contentType: false,
                    processData: false,
                    data: data
                });

ファイルアップロードコントローラー:

        <HttpPost> _
    Public Function UploadFile() As KeyValuePair(Of Boolean, String)
        Try
            If HttpContext.Current.Request.Files.AllKeys.Any() Then
                Dim httpPostedFile = HttpContext.Current.Request.Files("UploadedImage")
                Dim source = HttpContext.Current.Request.Form("Source").ToString()

Javascriptでわかるように、渡される追加データは「ソース」キーで、値は「1」です。そして、Chandrikaが上記で回答したように、コントローラーはこの渡されたデータを「System.Web.HttpContext.Current.Request.Form( "Source")。ToString()」を通して読み取ります。

コントローラコードはVB.netにあるため、Form( "Source")は()(vs. [])を使用することに注意してください。

お役に立てれば。

5
Sam

この方法で、複数のファイルと複数の属性を抽出できます。

public async Task<HttpResponseMessage> Post()
{
    Dictionary<string,string> attributes = new Dictionary<string, string>();
    Dictionary<string, byte[]> files = new Dictionary<string, byte[]>();

    var provider = new MultipartMemoryStreamProvider();
    await Request.Content.ReadAsMultipartAsync(provider);
    foreach (var file in provider.Contents)
    {
        if (file.Headers.ContentDisposition.FileName != null)
        {
            var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
            var buffer = await file.ReadAsByteArrayAsync();
            files.Add(filename, buffer);
        } else
        {
            foreach(NameValueHeaderValue p in file.Headers.ContentDisposition.Parameters)
            {
                string name = p.Value;
                if (name.StartsWith("\"") && name.EndsWith("\"")) name = name.Substring(1, name.Length - 2);
                string value = await file.ReadAsStringAsync();
                attributes.Add(name, value);
            }
        }
    }
    //Your code here  
    return new HttpResponseMessage(HttpStatusCode.OK);
}
3
Renzo Ciot

次の方法でそれを行うことができます:JQueryメソッド:

    var data = new FormData();

    data.append("file", filesToUpload[0].rawFile);
    var doc = {};            
    doc.DocumentId = 0; 
    $.support.cors = true;
    $.ajax({
        url: '/api/document/uploaddocument',
        type: 'POST',
        contentType: 'multipart/form-data',
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        success: function (response) {
            docId = response.split('|')[0];
            doc.DocumentId = docId;
            $.post('/api/document/metadata', doc)
                .done(function (response) {
                });
          alert('Document save successfully!');
        },
        error: function (e) {
            alert(e);
        }
    });

'UploadDocuement' Web APIを呼び出します

[Route("api/document/uploaddocument"), HttpPost]
        [UnhandledExceptionFilter]
        [ActionName("UploadDocument")]
        public Task<HttpResponseMessage> UploadDocument()
        {
            // Check if the request contains multipart/form-data.
            if (!Request.Content.IsMimeMultipartContent())
            {
                Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate()
                {
                    return new HttpResponseMessage()
                    {
                        StatusCode = HttpStatusCode.BadRequest,
                        Content = "In valid file & request content type!".ToStringContent()
                    };
                });
                return mytask;
            }


            string root = HttpContext.Current.Server.MapPath("~/Documents");
            if (System.IO.Directory.Exists(root))
            {
                System.IO.Directory.CreateDirectory(root);
            }
            var provider = new MultipartFormDataStreamProvider(root);

            var task = Request.Content.ReadAsMultipartAsync(provider).
            ContinueWith<HttpResponseMessage>(o =>
            {
                if (o.IsFaulted || o.IsCanceled)
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);

                FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);

                string guid = Guid.NewGuid().ToString();

                File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));

                string sFileName = provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "");

                FileInfo FInfos = new FileInfo(Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));

                Document dbDoc = new Document()
                {
                    DocumentID = 0                

                };

                context.DocumentRepository.Insert(dbDoc);
                context.Save();

                return new HttpResponseMessage()
                {
                    Content = new StringContent(string.Format("{0}|File uploaded.", dbDoc.DocumentID))
                };
            }
           );
            return task;

        }

次の方法でメタデータWeb APIを呼び出します。

[Route("api/document/metadata"), HttpPost]
        [ActionName("Metadata")]
        public Task<HttpResponseMessage> Metadata(Document doc)
        {
            int DocId = Convert.ToInt32(System.Web.HttpContext.Current.Request.Form["DocumentId"].ToString());

            Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("metadata updated")
                };
            });
            return mytask;
        }
1