Reputation: 580
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
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