2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ASP.Net Core PostリクエストでJsonと複数ファイルを同時に送信

Posted at

ASP.Net Coreで用意したAPIにJsonのリクエストのほかに、ファイルも同時にアップしたい要件があったので、
小さなプロジェクトに雑に載せました。

二つのライブラリをnugetで落としています。
Microsoft.AspNetCore.StaticFiles;
Newtonsoft.Json;

ソースコード
https://github.com/shinnosukekubo/ASP.NetCoreMultipleFileUpload

サーバープロジェクトを用意します。
image.png
image.png
image.png
適当にコントローラーを追加して
image.png

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace MulipleFileUpload.Server.Controllers
{
    [ApiController]
    [Route("api/[controller]/[action]")]
    public class JsonWithFileRequestController
    {
        private const long MaxFileSize = 1024L * 1024L * 1024L; // 1GB

        [HttpPost]
        [RequestSizeLimit(MaxFileSize)]
        [RequestFormLimits(MultipartBodyLengthLimit = MaxFileSize)]
        public async Task Upload(
            // json,fileList1などの引数名と、後述するクライアントでの指定名が一致する必要がある
            [FromForm] string json,
            [FromForm] IEnumerable<IFormFile> fileList1,
            [FromForm] IEnumerable<IFormFile> fileList2)
        {
            // Jsonパース
            TextModel model = JsonConvert.DeserializeObject<TextModel>(json);
            // 保存など
            foreach (var file in fileList1) 
            {

                var stream = file.OpenReadStream();
            }
        }

        class TextModel
        {
            public string Text { get; set; }
        }
    }
}

クライアントプロジェクトを用意
リクエストを投げるだけなのでコンソールアプリにしました。
image.png

using Microsoft.AspNetCore.StaticFiles;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;


namespace MultipleFileUpload.Client
{
    class Program
    {
        static HttpClient client;
        static async Task Main(string[] args)
        {
            client = new HttpClient();
            // ポートは書き換えてください
            client.BaseAddress = new Uri("https://localhost:44357/");

            // アップロードするファイルパス
            var filePath = @"C:\Users\shinn\Downloads\images.jpg";
    
                await PostWithFilesAsync(
                "api/JsonWithFileRequest/Upload",
                new { Test = "AAA" },
                new Dictionary<string, List<string>>()
                {
                    // 名前を付ければいくつでも送れるし、リストにしても送れる
                    // 適当に2つにリストに分けていれた
                    { "fileList1", new List<string>{ filePath, filePath } },
                    { "fileList2", new List<string>{ filePath, filePath } }
                });
        }


        public static async Task PostWithFilesAsync(string api, object request, Dictionary<string, List<string>> filePathsDictionary)
        {
            using var content = new MultipartFormDataContent();
            List<FileStream> streams = new List<FileStream>();
            try
            {
                // 先ずJsonをContentに含めた
                var requestJson = JsonConvert.SerializeObject(request);
                var jsonContent = new StringContent(requestJson, Encoding.UTF8, "application/json");
                jsonContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
                {
                    Size = requestJson.Length,
                    // コントローラーの関数の引数と一致させる
                    Name = "json",
                };
                content.Add(jsonContent);

                // 指定パスのファイルをContentに含める
                foreach (var filePaths in filePathsDictionary)
                {
                    var partName = filePaths.Key;
                    foreach (var filePath in filePaths.Value)
                    {
                        FileStream fs = File.OpenRead(filePath);
                        string fileName = Path.GetFileName(filePath);
                        content.Add(CreateFileContent(fs, partName, fileName));
                        streams.Add(fs);
                    }
                }
                // 送信
                await client.PostAsync(api, content);
            }
            finally
            {
                streams.ForEach(x => x.Close());
            }
        }

        private static StreamContent CreateFileContent(Stream stream, string partName, string fileName)
        {
            var fileContent = new StreamContent(stream);
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
            {
                Size = stream.Length,
                // コントローラーの関数の引数と一致させる
                Name = partName,
                FileName = fileName
            };

            // 拡張子から自動でcontent-typeを割り出してくれる
            // Microsoft.AspNetCore.StaticFilesのインポートが必要
            if (new FileExtensionContentTypeProvider().TryGetContentType(fileName, out string contentType))
            {
                fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
                return fileContent;
            }
            else
            {
                throw new Exception($"拡張子が不明なファイルが指定されました。ファイル名{fileName}");
            }
        }
    }
}

サーバーを起動した状態で、コンソールアプリを起動すれば、リクエストが飛ぶと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?