英文:
Send PDF file in HTTP POST request using ASP.NET Web API
问题
我使用ASP.NET Core Web API项目模板在Visual Studio中创建了一个Web API,允许客户端上传文件到我的应用程序。我创建了处理来自想要将PDF报告上传到服务器的客户端请求的控制器。以下是操作方法的代码:
[HttpPost("Report/{reportId}")]
public async Task<IActionResult> AttachReport(string reportId)
{
Reporter client = new Reporter();
await client.AttachReportToRequest(reportId);
return Ok(); // 这里是您的返回语句
}
这是实际发送请求到我的应用程序服务器的方法:
public async Task<HttpResponseMessage> AttachReportToRequest(string reportId)
{
try
{
if (authToken is null)
{
authToken = await login("username", "passw0rd");
}
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/to/app/server/reportInfo");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("app-JWT", authToken);
string scanReportFile = $"{Directory.GetCurrentDirectory()}\\Reports\\sample_report.pdf";
MultipartFormDataContent requestBody = new MultipartFormDataContent();
// 创建值的JSON负载
requestBody.Add(new StringContent("\"values\":{{\"ReportId\":\"{reportId}\",\"Description\":\"test\",\"Submitter\":\"John Smith\",\"Number of Attachment\":\"1\"}}"), "entry");
requestBody.Add(new StreamContent(File.OpenRead("C:/Users/myuser/Documents/ReporterApp/myreport.pdf")), "file-attachment", "C:/Users/myuser/Documents/ReporterApp/myreport.pdf");
requestMessage.Content = requestBody;
HttpResponseMessage attachmentResponse = await httpClient.SendAsync(requestMessage);
return attachmentResponse;
}
catch (Exception)
{
throw;
}
}
我遇到的问题是,每次httpClient尝试发送请求时,客户端都会超时。我从服务器那里从未收到任何HTTP响应。我尝试使用Postman从相同的端点使用相同的multipart/form-data内容类型访问相同的应用程序,它可以正常工作。是否有特定的原因,导致SendAsync方法在发送请求时超时?
我尝试将FileStream放在using块中,并尝试发送不同类型的文件,但这些方法都没有奏效。
英文:
I created a web API using the ASP.NET Core Web API project template in Visual Studio to allow clients to upload files to my application. I created the controller which handles request from clients that want to upload PDF reports to the server. Here is the code for the action method:
[HttpPost("Report/{reportId")]
public async Task<IActionResult> AttachReport(string reportId)
{
Reporter client = new Reporter();
await client.AttachReportToRequest(reportId);
return
}
This is the method that actually sends the request to my application server.
public async Task<HttpResponseMessage> AttachReportToRequest(string reportId)
{
try
{
if (authToken is null)
{
authToken = await login("username", "passw0rd");
}
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/to/app/server/reportInfo");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("app-JWT", authToken);
string scanReportFile = $"{Directory.GetCurrentDirectory()}\\Reports\\sample_report.pdf";
MultipartFormDataContent requestBody = new MultipartFormDataContent();
//Create values JSON payload
requestBody.Add(new StringContent($"\"values\":{{
\"ReportId\":\"{reportId}\",
\"Description\":\"test\",
\"Submitter\":\"John Smith\",
\"Number of Attachment\":\"1\"
}}"), "entry");
requestBody.Add(new StreamContent(File.OpenRead("C:/Users/myuser/Documents/ReporterApp/myreport.pdf")), "file-attachment", "C:/Users/myuser/Documents/ReporterApp/myreport.pdf");
requestMessage.Content = requestBody;
HttpResponseMessage attachmentResponse = await httpClient.SendAsync(requestMessage);
return attachmentResponse;
}
catch (Exception)
{
throw;
}
}
The issue I'm having is that every time the httpClient tries to send the request the client times out. I never receive any kind of HTTP response from the server. I tried hitting the same application using the same endpoint and the same multipart/form-data content type from Postman and it works. Is there any particular reason why the SendAsync method would be timing out when sending the request?
I tried putting the FileStream in a using block and tried sending different file types but none of those things worked.
答案1
得分: 3
以下是您要翻译的内容:
经过许多、许多个漫长的小时,我终于能够弄清楚了。原来我的代码有一些问题。我最初以为Content-Type标头的边界参数有问题。事实证明,.NET Framework会自动处理为您生成边界的工作。
我错过的第一件事是,虽然MultipartFormDataContent是HttpContent的子类,但它实际上是HttpContent对象的容器。将单个HttpContent对象附加到请求和附加MultipartFormDataContent类型的对象之间的一个关键区别是,对于您添加到容器的每个对象,您需要指定一个Content-Type标头;没有为每个对象提供该标头,服务器将不知道每个部分的类型。例如,在对requestBody.Add()的第一次调用中,我正在传递一个带有JSON负载字符串的StringContent对象。但是,StringContent构造函数有一种可以指定编码和媒体类型的重载。没有"application/json",StringContent对象默认为"text/plain"媒体类型,如果服务器期望JSON,则无法工作。这样做解决了第一个问题:
requestBody.Add(new StringContent("...", Encoding.UTF8, "application/json"), "entry")
第二个问题基本上与第一个问题相同。与在调用requestBody.Add()时内联创建StreamContent对象不同,我可以首先将StreamContent对象分配给一个变量,然后添加必要的标头。由于我发送的文件类型是pdf,正确的媒体类型是"application/octet-stream"。这是对我有用的。
StreamContent myContent = new StreamContent(File.OpenRead("C:/User/.../report.pdf"));
myContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
requestBody.Add(myContent, "attachment");
一旦我这样做了,HttpClient成功发送了请求,不再超时。您还可以使用File.ReadAllBytes()方法做类似的事情:
ByteArrayContent fileContents = new ByteArrayContent(File.ReadAllBytes("path/to/file"));
fileContents.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
这也应该可以工作。
英文:
After many, many long hours, I was finally able to figure it out. It turns out there are actually a few things wrong with my code. I initially thought there was an issue with the boundary parameter of the Content-Type header. As it turns out, the .NET Framework automatically takes care of generating the boundary for you.
The first thing I missed was that, even though MultipartFormDataContent is a subclass of HttpContent, it is actually a container of HttpContent objects. A key difference between attaching a single HttpContent object to your request and attaching an object of type MultipartFormDataContent is that for every object you add to the container you need to specify a Content-Type header; without that header for each object the server won't know what the type of each part is. For example, in the first call to requestBody.Add() I am passing it a StringContent object with the JSON payload string. However, the StringContent constructor has an overload where you can specify the encoding and media type. Without the "application/json", the StringContent object defaults to a media type of "text/plain", which won't work if the server is expecting JSON. Doing this solves the first problem:
requestBody.Add(new StringContent($"...", Encoding.UTF8, "application/json"), "entry")
The second issue was pretty much the same as the first. Rather than create the StreamContent object inline with the call to requestBody.Add(), I could first assign the StreamContent object to a variable and then add the necessary headers to it. Since the type of file I'm sending is a pdf, the correct media type is "application/octet-stream". This is what worked for me.
StreamContent myContent = new StreamContent(File.OpenRead("C:/User/.../report.pdf"));
myContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
requestBody.Add(myContent, "attachment");
Once I did this, the HttpClient sent the request successfully and no longer timed out. You could also do something like this with the File.ReadAllBytes() method:
ByteArrayContent fileContents = new ByteArrayContent(File.ReadAllBytes("path/to/file"));
fileContents.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
This should also work.
答案2
得分: 0
尝试不同的方法(或使用RestSharp库 - 更清晰)
Uri uri = new Uri($"{_BaseUrl}api/endpoint");
HttpRequestMessage hrm = GetDefaultRequestMessage(HttpMethod.Post, uri);
var reqContent = new MultipartFormDataContent();
reqContent.Add(new StringContent(reportID), "ReportId");
//...
string fileName = "report.txt";
MemoryStream ms = new(System.Text.Encoding.UTF8.GetBytes(fileContent.ToString()));
var bytes = new Byte[ms.Length];
ms.Read(bytes, 0, bytes.Length);
reqContent.Add(new ByteArrayContent(bytes), "Files", fileName);
hrm.Content = reqContent;
var response = await this._client.SendAsync(hrm);
英文:
try different approach (or use RestSharp library - much cleaner)
Uri uri = new Uri($"{_BaseUrl}api/endpoint");
HttpRequestMessage hrm = GetDefaultRequestMessage(HttpMethod.Post, uri);
var reqContent = new MultipartFormDataContent();
reqContent.Add(new StringContent(reportID), "ReportId");
//...
string fileName = $"report.txt";
MemoryStream ms = new(System.Text.Encoding.UTF8.GetBytes(fileContent.ToString()));
var bytes = new Byte[ms.Length];
ms.Read(bytes, 0, bytes.Length);
reqContent.Add(new ByteArrayContent(bytes), "Files", fileName);
hrm.Content = reqContent;
var response = await this._client.SendAsync(hrm);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论