Upload file with non-ASCII filename in Http request via multipart/form-data in Logic App STD
Background
For sending multipart/form-data content in Logic App, we have an official document which introduce a way to achieve it: Create workflows that call external endpoints or other workflows – Azure Logic Apps | Microsoft Learn.
But recently we found that if Content-Disposition contains non-ASCII characters (Chinese, Japanese characters) in filename section , the filename will be shown as ????.<extension> if we implement the Http action as per the document.
Mechanism
Via testing with CURL command, we have an alternative way to send multipart/form-data by encoding all payload as base64 encoded string, sample payload sent by CURL is following:
{
“$content-type”: “multipart/form-data; boundary=————————boundary”,
“$content”: “base64 encoded content”
}
Decoded base64 string is a normal multipart/form-data format:
————————–boundary
Content-Disposition: form-data; name=”filename”; filename=”[File name]”
Content-Type: application/octet-stream
File content binary
————————–boundary–
Implementation
As per the mechanism, we can know that it is also possible to use the same format to send multipart form via using Logic App Http action, so the only road block is how to combine boundary header, file binary and boundary footer in to a single base64 string.
In most of the situation, we get file content as a separate base64 string, as we all know, multiple base64 string cannot be concatenated directly which could cause decode incorrectly.
So we have to convert all 3 parts into binary first, combine them into a single byte[], then convert to base64 string all together, which we don’t have any built-in expressions/features can handle it.
As a workaround, we need to prepare 3 base64 encoded strings (boundary header, file content and boundary footer), thencan use the new feature “C# inline” to achieve this approach, sample code is following:
// Add the required libraries
#r “Newtonsoft.Json”
#r “Microsoft.Azure.Workflows.Scripting”
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Workflows.Scripting;
using Newtonsoft.Json.Linq;
public static async Task<Results> Run(WorkflowContext context, ILogger log)
{
var fileContent = (await context.GetActionResults(“FileContent”).ConfigureAwait(false)).Outputs;
string header = (await context.GetActionResults(“BoundaryHeader”).ConfigureAwait(false)).Outputs;
string footer = (await context.GetActionResults(“BoundaryFooter”).ConfigureAwait(false)).Outputs;
byte[] headerBinary = Convert.FromBase64String(header.ToString());
byte[] footerBinary = Convert.FromBase64String(footer.ToString());
byte[] fileContentBinary = Convert.FromBase64String(fileContent.ToString());
List<byte> allBytes = new List<byte>();
allBytes.AddRange(headerBinary);
allBytes.AddRange(fileContentBinary);
allBytes.AddRange(footerBinary);
string base64Content = Convert.ToBase64String(allBytes.ToArray());
return new Results
{
Message = base64Content
};
}
public class Results
{
public string Message {get; set;}
}
After we get the output from C# action, we can compose all of them in Http action
Microsoft Tech Community – Latest Blogs –Read More