Search code examples
c#.netstreamfilenames

How to read inline type File in .Net 8 C#


I am trying to upload the image without a filename. The API received null in the file parameter.

The Request Send code (client side):

var content = new MultipartFormDataContent
{
    { new StreamContent(file), "file" },
    { new StringContent(JsonConvert.SerializeObject(metadata), Encoding.UTF8, "application/json"), "metadata" }
};

Request Receive code (API With .Net 8):

     var form = await Request.ReadFormAsync();
     var formFile = form.Files["file"];

//The formFile is returned as NULL

If I supply the filename in the stream content then it does work. But my requirement is to make it work without providing the filename.


Solution

  • I've found the solution, we need to change the api code as:

    // Create a minimal API endpoint that handles a POST
    // We use the default configured JsonOptions for deserializing the JSON
    app.MapPost("/", async (HttpContext ctx, IOptions<JsonOptions> jsonOptions) =>
    {
        // make sure we have the correct header type
        if (!MediaTypeHeaderValue.TryParse(ctx.Request.ContentType, out MediaTypeHeaderValue? contentType)
            || !contentType.MediaType.Equals("multipart/form-data", StringComparison.OrdinalIgnoreCase))
        {
            return Results.BadRequest("Incorrect mime-type");
        }
    
        // Variables for holding the data parsed from the response
        MyData? jsonData = null;
        byte[]? binaryData = null;
    
        // Get the multipart/form-boundary header from the content-type
        // Content-Type: multipart/form-data; boundary="--73dc24e0-b350-48f8-931e-eab338df00e1"
        // The spec says 70 characters is a reasonable limit.
        string boundary = GetBoundary(contentType, lengthLimit: 70);
        var multipartReader = new MultipartReader(boundary, ctx.Request.Body);
        
        // Use the multipart reader to read each of the sections
        while (await multipartReader.ReadNextSectionAsync(ct) is { } section)
        {
            // Make sure we have a content-type for the section
            if(!MediaTypeHeaderValue.TryParse(section.ContentType, out MediaTypeHeaderValue? sectionType))
            {
                return Results.BadRequest("Invalid content type in section " + section.ContentType);
            }
    
            if (sectionType.MediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase))
            {
                // If the section is JSON, deserialize directly from the section stream
                // using the default JSON serialization options configured for the app
                jsonData = await JsonSerializer.DeserializeAsync<MyData>(
                    section.Body,
                    jsonOptions.Value.JsonSerializerOptions,
                    cancellationToken: ctx.RequestAborted);
            }
            else if (sectionType.MediaType.Equals("application/octet-stream", StringComparison.OrdinalIgnoreCase))
            {
                // If the section is binary data, deserialize into an array
                // there are potentially more efficient things we could do here 
                // depending on how you need the data
                using var ms = new MemoryStream();
                await section.Body.CopyToAsync(ms, ctx.RequestAborted);
                binaryData = ms.ToArray();
            }
            else
            {
                return Results.BadRequest("Invalid content type in section " + section.ContentType);
            }
        }
    
        // Just printing it out for debugging purposes
        app.Logger.LogInformation("Receive Json {JsonData} and binary data {BinaryData}",
            jsonData, Convert.ToBase64String(binaryData));
    
        return Results.Ok();
        
        // Retrieves the boundary marker from the content-type, handling quotes etc 
        // Taken from https://github.com/dotnet/aspnetcore/blob/4eef6a1578bb0d8a4469779798fe9390543d15c0/src/Http/Http/src/Features/FormFeature.cs#L318-L320
        static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
        {
            var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
            if (StringSegment.IsNullOrEmpty(boundary))
            {
                throw new InvalidDataException("Missing content-type boundary.");
            }
            if (boundary.Length > lengthLimit)
            {
                throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
            }
            return boundary.ToString();
        }
    });
    

    Source of code : https://andrewlock.net/reading-json-and-binary-data-from-multipart-form-data-sections-in-aspnetcore/