分段文件上传问题

分段文件上传问题

本文介绍了.net 5 Azure Function 分段文件上传问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将 Azure 功能从 .net 3.1 升级到 5,但遇到了一些困难.许多事情从 3.1 更改为 5,我正在尝试找出细微差别.我有一个接受多部分/表单数据帖子的函数,它是这样创建的:

I'm upgrading an Azure function from .net 3.1 to 5 and am having some difficulties. Many things changed from 3.1 to 5 and I'm trying to figure out the nuances. I have a function that accepts a multipart/form-data post which is created like this:

public class EmailService
    {
        private EmailPost _post;

        public EmailService() { }

        public EmailService(EmailPost post)
        {
            _post = post;
        }

        public EmailPost Post => _post;

        public async Task<EmailServiceResult> SendAsync()
        {
            try
            {
                var httpClient = new HttpClient();
                var form = new MultipartFormDataContent();

                form.Add(new StringContent(string.Join(",", _post.To)), "To");
                form.Add(new StringContent(string.Join(",", _post.Cc)), "Cc");
                form.Add(new StringContent(string.Join(",", _post.Bcc)), "Bcc");
                form.Add(new StringContent(_post.From), "From");
                form.Add(new StringContent(_post.Subject), "Subject");
                form.Add(new StringContent(_post.Body), "Body");
                form.Add(new StringContent(_post.IsHtml.ToString()), "IsHtml");
                form.Add(new StringContent(_post.IsPriority.ToString()), "IsPriority");

                // any files?

                foreach (var i in _post.Files)
                {
                    if (!File.Exists(i))
                    {
                        throw new Exception("File does not exist. Make sure it's a full path.");
                    }

                    var filename = i.Substring(i.LastIndexOf("\", StringComparison.InvariantCultureIgnoreCase) + 1);
                    var fs = new FileStream(i, FileMode.Open, FileAccess.Read);
                    var fileByteArray = new byte[fs.Length];
                    fs.Read(fileByteArray, 0, fileByteArray.Length);
                    form.Add(new ByteArrayContent(fileByteArray, 0, fileByteArray.Length), "filename", filename);
                    fs.Close();
                }

                var endPointUrl = "my az function endpoint url";

                // if it's local, then use the test version
                if (Debugger.IsAttached)
                    endPointUrl = "local url here";

                var response = await httpClient.PostAsync(endPointUrl, form);

                response.EnsureSuccessStatusCode();
                httpClient.Dispose();
            }
            catch (Exception ex)
            {
                return new EmailServiceResult
                {
                    Success = false,
                    Message = ex.Message
                };
            }

            return new EmailServiceResult {Success = true};
        }
    }

然后是 Azure 函数:

Then the Azure function:

    public class Email
    {
        private ILogger _log;
        private IConfiguration _config;

        public Email(IConfiguration config, ILogger<Email> logger)
        {
            _config = config;
            _log = logger;
        }

        [Function("Email")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = "email")]HttpRequestData req, FunctionContext context)
        {
            try
            {
                // Suck the request body into a stream
                var ms = new MemoryStream();
                await req.Body.CopyToAsync(ms);
                ms.Position = 0;

                // look through all the keys
                foreach (var h in req.Headers)
                    _log.LogInformation(h.Key + ":" + string.Join(',', h.Value));

                var streamContent = new StreamContent(ms);

                // This line was used with 3.1 but isn't available any more because the req object is different (HttpRequestData in v5 vs HttpRequest in v3)
                // streamContent.Headers.ContentType = req.Content.Headers.ContentType;
                // so I did it this way instead
                var ct = req.Headers.FirstOrDefault(o => o.Key == "Content-Type").Value.FirstOrDefault().Split(';');

                // this line is the kicker. it doesn't accept a value with a boundary, which is needed later
                // multipart/form-data; boundary="b0ff40b2-8b47-4746-b85b-a876924e4e4c"
                // so I just pass in: multipart/form-data
                var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue(ct[0]);
                streamContent.Headers.ContentType = mthv;

                // this line needs the boundary, which I can't figure out how to enter.
                var provider = await streamContent.ReadAsMultipartAsync();

                // ... more processing of fields
                return req.CreateResponse(System.Net.HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
               ///... error handling
            }
        }
    }

然后为了完整起见,这是我的程序文件:

And then for completeness, here's my Program file:

public class Program
    {
        public static async Task Main(string[] args)
        {
            var host = new HostBuilder()
                .ConfigureAppConfiguration(c =>
                {
                    c.SetBasePath(Environment.CurrentDirectory);
                    c.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
                    // Add Environment Variables since we need to get the App Configuration connection string from settings.
                    c.AddEnvironmentVariables();
                    // Call Build() so we can get values from the IConfiguration.
                    var config = c.Build();
                })
                .ConfigureFunctionsWorkerDefaults()
                .ConfigureServices(s =>
                {
                    // s.AddSingleton<IHttpResponderService, DefaultHttpResponderService>();
                })
                .Build();

            await host.RunAsync();
        }

我很难弄清楚如何使用:var provider = await streamContent.ReadAsMultipartAsync();需要边界但是:var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue(ct[0]);不允许有边界.

I'm having a tough time figuring out how to use:var provider = await streamContent.ReadAsMultipartAsync();which needs the boundary but:var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue(ct[0]);doesn't allow for a boundary.

我得到的错误是:提供的HttpContent"实例无效.它没有带有边界"参数的多部分"内容类型标头.(参数'内容')

The error I get is: Invalid 'HttpContent' instance provided. It does not have a 'multipart' content-type header with a 'boundary' parameter. (Parameter 'content')

如果有人对这个特定问题有任何建议,我将不胜感激.或者,如果有另一种方法可以完全做到这一点,那也没关系.谢谢!

If anyone has some advice on this specific issue I would appreciate it. Or if there's another way to do it completely, that would also be fine. Thanks!

推荐答案

Media-type headers 由带有可选参数的 media-type 组成.MediaTypeHeaderValue 的构造函数只接受媒体类型;参数应通过 Parameters 属性:

Media-type headers are made up of a media-type with optional parameters. The constructor for MediaTypeHeaderValue only takes a media-type; the parameters should be set via the Parameters property:

var mthv = new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data");
mthv.Parameters.Add(new NameValueHeaderValue("boundary", "b0ff40b2-8b47-4746-b85b-a876924e4e4c"));
streamContent.Headers.ContentType = mthv;

如果您需要解析传入请求中的 boundary 值并使用相同的值,那么我建议您查看 MediaTypeHeaderValue.Parse.

If you need to, say, parse the boundary value on the incoming request and use the same value, then I'd recommend looking into MediaTypeHeaderValue.Parse.

这篇关于.net 5 Azure Function 分段文件上传问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-05 19:53