From 3b0e1b73cdccd9462915edc116b8d8ea32457d08 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 26 Apr 2022 14:18:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(wxapi):=20=E4=BF=AE=E5=A4=8D=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E7=B4=A0=E6=9D=90=E6=96=87=E4=BB=B6=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=20Unicode=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8D=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...atApiClientExecuteCardInvoiceExtensions.cs | 9 +--- ...iClientExecuteCgibinComponentExtensions.cs | 10 +---- ...piClientExecuteCgibinMaterialExtensions.cs | 13 +----- ...atApiClientExecuteCgibinMediaExtensions.cs | 18 +------- ...ApiClientExecuteCustomServiceExtensions.cs | 9 +--- .../WechatApiClientExecuteShopExtensions.cs | 9 +--- .../WechatApiClientExecuteWxaExtensions.cs | 19 +------- .../Internal/FileHttpContentBuilder.cs | 45 +++++++++++++++++++ 8 files changed, 55 insertions(+), 77 deletions(-) create mode 100644 src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/Internal/FileHttpContentBuilder.cs diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCardInvoiceExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCardInvoiceExtensions.cs index 534cbbdb..9511adb7 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCardInvoiceExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCardInvoiceExtensions.cs @@ -354,14 +354,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .CreateRequest(request, HttpMethod.Post, "card", "invoice", "platform", "setpdf") .SetQueryParam("access_token", request.AccessToken); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var httpContent = new MultipartFormDataContent(boundary); - using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty()); - httpContent.Add(fileContent, "\"pdf\"", "\"invoice.pdf\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/pdf"); - fileContent.Headers.ContentLength = request.FileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "invoice.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", formDataName: "pdf"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinComponentExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinComponentExtensions.cs index d69fd504..2bab3bcb 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinComponentExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinComponentExtensions.cs @@ -387,16 +387,8 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .CreateRequest(request, HttpMethod.Post, "cgi-bin", "component", "uploadprivacyextfile") .SetQueryParam("access_token", request.AccessToken); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty()); - using var httpContent = new MultipartFormDataContent(boundary); - httpContent.Add(fileContent, "\"file\"", $"\"{HttpUtility.UrlEncode(request.FileName)}\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(request.FileContentType); - fileContent.Headers.ContentLength = request.FileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "file"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } - } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMaterialExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMaterialExtensions.cs index d3d67ad9..f2c05ebb 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMaterialExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMaterialExtensions.cs @@ -84,20 +84,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .SetQueryParam("access_token", request.AccessToken) .SetQueryParam("type", request.Type); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty()); - using var descContent = new ByteArrayContent(Encoding.UTF8.GetBytes(client.JsonSerializer.Serialize(request))); - using var httpContent = new MultipartFormDataContent(boundary); - httpContent.Add(fileContent, "\"media\"", $"\"{HttpUtility.UrlEncode(request.FileName)}\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(request.FileContentType); - fileContent.Headers.ContentLength = request.FileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media"); if (TYPE_VIDEO.Equals(request.Type)) { - httpContent.Add(descContent, "\"description\""); + httpContent.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(client.JsonSerializer.Serialize(request))), "\"description\""); } - return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMediaExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMediaExtensions.cs index 1f565cc1..7b4ec00a 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMediaExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMediaExtensions.cs @@ -62,14 +62,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .SetQueryParam("access_token", request.AccessToken) .SetQueryParam("type", request.Type); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty()); - using var httpContent = new MultipartFormDataContent(boundary); - httpContent.Add(fileContent, "\"media\"", $"\"{HttpUtility.UrlEncode(request.FileName)}\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(request.FileContentType); - fileContent.Headers.ContentLength = request.FileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } @@ -121,14 +114,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .CreateRequest(request, HttpMethod.Post, "cgi-bin", "media", "uploadimg") .SetQueryParam("access_token", request.AccessToken); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty()); - using var httpContent = new MultipartFormDataContent(boundary); - httpContent.Add(fileContent, "\"media\"", $"\"{HttpUtility.UrlEncode(request.FileName)}\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(request.FileContentType); - fileContent.Headers.ContentLength = request.FileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCustomServiceExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCustomServiceExtensions.cs index 25a3cbb7..618a663d 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCustomServiceExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCustomServiceExtensions.cs @@ -93,14 +93,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .SetQueryParam("access_token", request.AccessToken) .SetQueryParam("kf_account", request.KfAccount); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var fileContent = new ByteArrayContent(request.HeadImageFileBytes ?? Array.Empty()); - using var httpContent = new MultipartFormDataContent(boundary); - httpContent.Add(fileContent, "\"media\"", "\"image.jpg\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg"); - fileContent.Headers.ContentLength = request.HeadImageFileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.jpg", fileBytes: request.HeadImageFileBytes, fileContentType: "image/jpeg", formDataName: "media"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteShopExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteShopExtensions.cs index 7db2639f..4d01bf94 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteShopExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteShopExtensions.cs @@ -39,14 +39,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api { flurlReq.SetQueryParam("upload_type", 0); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var httpContent = new MultipartFormDataContent(boundary); - using var fileContent = new ByteArrayContent(request.ImageFileBytes ?? Array.Empty()); - httpContent.Add(fileContent, "\"media\"", "\"image.png\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png"); - fileContent.Headers.ContentLength = request.ImageFileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.ImageFileBytes, fileContentType: "image/png", formDataName: "media"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs index cb17cc31..d072b0f9 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaExtensions.cs @@ -390,14 +390,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .CreateRequest(request, HttpMethod.Post, "wxa", "img_sec_check") .SetQueryParam("access_token", request.AccessToken); - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty()); - using var httpContent = new MultipartFormDataContent(boundary); - httpContent.Add(fileContent, "\"media\"", "\"image.png\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png"); - fileContent.Headers.ContentLength = request.FileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.FileBytes, fileContentType: "image/png", formDataName: "media"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } @@ -781,15 +774,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api .CreateRequest(request, HttpMethod.Post, "wxa", "imagesearch") .SetQueryParam("access_token", request.AccessToken); - - string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); - using var httpContent = new MultipartFormDataContent(boundary); - using var fileContent = new ByteArrayContent(request.ImageFileBytes ?? Array.Empty()); - httpContent.Add(fileContent, "\"img\"", "\"image.png\""); - httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary); - fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png"); - fileContent.Headers.ContentLength = request.ImageFileBytes?.Length; - + using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.ImageFileBytes, fileContentType: "image/png", formDataName: "img"); return await client.SendRequestAsync(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken); } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/Internal/FileHttpContentBuilder.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/Internal/FileHttpContentBuilder.cs new file mode 100644 index 00000000..3853d482 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/Internal/FileHttpContentBuilder.cs @@ -0,0 +1,45 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; + +namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities +{ + internal static class FileHttpContentBuilder + { + public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string formDataName) + { + return Build(fileName: fileName, fileBytes: fileBytes, fileContentType: fileContentType, formDataName: formDataName, (_) => { }); + } + + public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string formDataName, Action configureFileHttpContent) + { + if (fileName == null) throw new ArgumentNullException(nameof(fileName)); + if (formDataName == null) throw new ArgumentNullException(nameof(formDataName)); + if (configureFileHttpContent == null) throw new ArgumentNullException(nameof(configureFileHttpContent)); + + fileName = fileName.Replace("\"", ""); + fileBytes = fileBytes ?? Array.Empty(); + fileContentType = string.IsNullOrEmpty(fileContentType) ? "application/octet-stream" : fileContentType; + formDataName = formDataName.Replace("\"", ""); + + // HACKED: 默认不支持 Unicode 文件名 https://github.com/dotnet/runtime/issues/22996 + byte[] bytesFileName = Encoding.UTF8.GetBytes(fileName); + char[] bytesHackedFileName = new char[bytesFileName.Length]; + Array.Copy(bytesFileName, 0, bytesHackedFileName, 0, bytesFileName.Length); + string hackedFileName = new string(bytesHackedFileName); + + ByteArrayContent fileContent = new ByteArrayContent(fileBytes); + fileContent.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=\"{formDataName}\"; filename=\"{hackedFileName}\""); + fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileContentType); + fileContent.Headers.ContentLength = fileBytes.Length; + configureFileHttpContent(fileContent); + + string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); + MultipartFormDataContent httpContent = new MultipartFormDataContent(boundary); + httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/form-data; boundary={boundary}"); + httpContent.Add(fileContent); + return httpContent; + } + } +}