Quarkus环境下通过内部中转API向Redmine API上传文件的实现问题
Quarkus环境下通过内部中转API向Redmine API上传文件的实现问题
兄弟,太懂你这种中转文件卡壳的憋屈了!之前帮朋友调过几乎一模一样的Quarkus中转上传场景,踩了好几个坑,给你捋几个靠谱的思路和代码示例,应该能帮你搞定。
首先得明确:Redmine的文件上传接口要求用multipart/form-data格式,核心参数是attachments[][file](用来传文件),还要带上issue_id这类必要参数。咱们的中转API必须严格按照这个格式把请求原封不动(或者对应上参数名)转发给Redmine,这是核心前提。
下面给你两种亲测有效的实现方式:
方式一:直接接收Multipart请求并转发
这种方式最直观,前端传过来的是multipart格式,咱们直接接过来再转交给内部中转API。
1. 编写文件上传的Resource类
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.resteasy.reactive.MultipartFormData; import org.jboss.resteasy.reactive.FilePart; @Path("/proxy-redmine-upload") public class RedmineUploadProxyResource { // 注入调用内部中转API的RestClient @RestClient private InternalRedmineProxyClient internalProxyClient; @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) public Response proxyFileUpload(MultipartFormData formData) { // 从前端请求里拿必要参数,比如issue_id String issueId = formData.getFormDataPart("issue_id", String.class, null); // 获取上传的文件部分 FilePart filePart = formData.getFile("file").get(); // 构造要转发给内部API的multipart数据,严格对应Redmine要求的参数名 MultipartFormData.Builder forwardFormBuilder = MultipartFormData.create(); forwardFormBuilder.field("issue_id", issueId); // 注意这里的参数名是Redmine要求的attachments[][file] forwardFormBuilder.file("attachments[][file]", filePart.getFileName(), filePart.getMediaType(), filePart.getInputStream()); // 调用内部中转API Response redmineResp = internalProxyClient.forwardToRedmine(forwardFormBuilder.build()); // 把Redmine的响应原封不动返回给前端 return Response.status(redmineResp.getStatus()) .entity(redmineResp.readEntity(String.class)) .build(); } }
2. 定义内部中转API的RestClient接口
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import org.jboss.resteasy.reactive.MultipartFormData; @RegisterRestClient(baseUri = "http://你的内部中转API地址") public interface InternalRedmineProxyClient { @POST @Path("/redmine-api-proxy/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) Response forwardToRedmine(MultipartFormData formData); }
方式二:接收InputStream再构造Multipart转发
如果你之前是用InputStream接收前端文件的,那可以手动构造符合要求的multipart请求再转发:
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.HeaderParam; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.resteasy.reactive.MultipartFormData; import java.io.InputStream; @Path("/upload-stream-proxy") public class StreamUploadProxyResource { @RestClient private InternalRedmineProxyClient internalProxyClient; @POST @Consumes(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_JSON) public Response proxyStreamUpload(@HeaderParam("Content-Disposition") String contentDisposition, @HeaderParam("X-Issue-Id") String issueId, InputStream fileStream) { // 从Content-Disposition头里解析文件名,格式一般是form-data; name="file"; filename="test.pdf" String fileName = contentDisposition.split("filename=")[1].replace("\"", ""); // 手动构造Redmine需要的multipart格式 MultipartFormData formData = MultipartFormData.create() .field("issue_id", issueId) .file("attachments[][file]", fileName, MediaType.APPLICATION_OCTET_STREAM_TYPE, fileStream) .build(); Response redmineResp = internalProxyClient.forwardToRedmine(formData); return Response.status(redmineResp.getStatus()) .entity(redmineResp.readEntity(String.class)) .build(); } }
几个必须注意的坑
- 依赖要加全:Quarkus处理multipart和RestClient转发需要对应的扩展,pom.xml里一定要加:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-multipart</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest-client-reactive-multipart</artifactId> </dependency>
- 不要乱转格式:别把文件转成base64再传,Redmine不吃这套,必须用二进制的multipart格式直接转发。
- 资源泄漏要避免:如果手动处理InputStream,最好用try-with-resources包裹,Quarkus虽然会自动处理,但自己注意更稳妥。
- 认证信息要传递:如果内部中转API需要认证(比如Redmine的API密钥),要么在RestClient里通过@HeaderParam加,要么写个ClientRequestFilter统一处理。
你之前试的InputStream方式大概率是没正确构造Redmine要求的multipart格式才失败的,按照上面的示例调整一下,应该就能顺利中转了!
备注:内容来源于stack exchange,提问作者Wyatt
相关产品推荐
相关产品推荐

