Steve Boniface
Steve Boniface

Reputation: 580

Multipart/form-data request -- Uploading pdf is resulting in a blank file

I have an objective to send a pdf file from one server to a REST API which handles some archiving. I am using .NET Core 3.1 and the RestEase API library to help with some of the abstraction.

I have a simple console app that runs at a certain time everyday. The relevant code is as follows:

using System;
using System.Linq;
using System.Threading.Tasks;
using RestEase;

namespace CandidateUploadFile
{
    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                var apiClientBuilder = new ApiClientBuilder<ITestApi>();
                var api = apiClientBuilder.GetApi("https://my.api.com");
                var candidate = await api.GetCandidateByEmailAddress("[email protected]");
                var fileName = "tester.pdf";
                var fileBytesToUpload = await FileHelper.GetBytesFromFile($@"./{fileName}");
                var result = await api.UploadCandidateFileAsync(fileBytesToUpload, candidate.Data.First().Id, fileName);
            }
            catch (Exception e)
            {
                System.Console.WriteLine(e);
            }
        }
    }
}

apiClientBuilder does some auth-header adding, and that's really it. I'm certain that bit isn't relevant.

ITestApi looks like this:

using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Models;
using RestEase;

namespace CandidateUploadFile
{
    public interface ITestApi : IApi
    {
        [Get("v1/candidates/{candidateId}")]
        Task<Models.Response<Candidate>> GetCandidate([Path] string candidateId);

        [Get("v1/candidates")]
        Task<Models.Response<IEnumerable<Candidate>>> GetCandidateByEmailAddress([Query] string email);

        [Get("v1/candidates")]
        Task<Models.Response<IEnumerable<Candidate>>> GetCandidates();

        [Post("v1/candidates/{candidateId}/files?perform_as=327d4d21-5cb0-4bc7-95f5-ae43aabc2db7")]
        Task<string> UploadFileAsync([Path] string candidateId, [Body] HttpContent content);

        [Get("v1/users")]
        Task<Models.Response<IEnumerable<User>>> GetUsers();
    }
}

It's UploadFileAsync that is really relevant here.

You'll note from Program.Main that I don't explicitly invoke UploadFileAsync. I instead invoke an extension method that basically wraps UploadFileAsync for the purpose of uploading the pdf using a multipart/form-data request. This approach is what comes as a recommendation in the RestEase library docs.. That extension method looks like this:

using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace CandidateUploadFile
{
    public static class ApiExtension
    {
        public static async Task<string> UploadCandidateFileAsync(this ITestApi api, byte[] data, string candidateId, string fileName)
        {
            var content = new MultipartFormDataContent();

            var fileContent = new ByteArrayContent(data);
            fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
            {
                Name = "file",
                FileName = fileName
            };

            content.Add(fileContent);

            return await api.UploadFileAsync(candidateId, content);
        }
    }
}

So what will happen when my console app executes is: I will get a successful response from the upload endpoint, and the file on the archive server gets created, but it's blank.

It may be important to know that this does not happen when I send, say, a .txt file. The .txt file will save with the expected content.

Any insight would be helpful. I'm not sure where to start on this one.

Thank you!

Upvotes: 3

Views: 9585

Answers (1)

Steve Boniface
Steve Boniface

Reputation: 580

The issue was due to what I was doing in my GetBytesFromFile static helper method.

My static helper was using UTF-8 encoding to encode the binary content in the .pdfs I was uploading. However, it was working fine with .txt files I was uploading, which can be expected.

Lesson learned: there is no need -- and makes no sense -- to try to encode binary content before assign it to the multipart/form-data content. I just had to "pass-through" the binary content as-is, more-or-less.

Upvotes: 1

Related Questions