发送二进制文件的multipart/form-data到Trello (API)的Quarkus REST客户端。

huangapple go评论58阅读模式
英文:

Sending multipart/form-data with binary file with Quarkus REST client to Trello (API)

问题

我尝试更新旧的Java软件并将其移植到Quarkus与RESTEASY / Reactive / Microprofile,以在Trello卡片上添加图像(Trello文档:在卡片上创建附件)。请注意:文档存在一些不足之处,不能完全被视为“真实”。

为此,我必须发送一个包含一些属性(名称、文件名、MIME类型、setCover和二进制文件)的multipart/form-data。重点是以BINARY形式发送文件。

我的旧软件正常运行:

String name = "Cover.jpg";
File tempImage = new File(name);
try {

    Client client = ClientBuilder.newClient(new ClientConfig())
            .register(MultiPartFeature.class);

    String mime = "image/jpg";

    byte[] buffer = Files.readAllBytes(Paths.get("/PATH/" + name));

    // 保存临时文件
    FileOutputStream fos = new FileOutputStream(tempImage);
    fos.write(buffer);
    fos.flush();
    fos.close();

    FileDataBodyPart filePart = new FileDataBodyPart("file", tempImage);

    FormDataMultiPart formDataMultiPart = new FormDataMultiPart();
    final FormDataMultiPart multipart = (FormDataMultiPart) formDataMultiPart
            .field("mimeType", mime)
            .field("setCover", "true")
            .field("name", name)
            .bodyPart(filePart);

    // 添加封面
    WebTarget coverService = client.target(URI.create("https://api.trello.com/1/cards/CARD_ID/attachments"));
    String coverResponse = coverService
            .queryParam("key", "KEY")
            .queryParam("token", "TOKEN")
            .request()
            .header("Accept", "application/json")
            .post(Entity.entity(multipart, multipart.getMediaType()), String.class);
}

这会产生类似于以下内容的内容:

1 * 在主线程上发送客户端请求
1 > POST https://api.trello.com/1/cards/CARD_ID/attachments?key=KEY&token=TOKEN
1 > Accept: application/json
1 > Content-Type: multipart/form-data
--Boundary_1_2089589253_1678121120116
Content-Type: text/plain
Content-Disposition: form-data; name="mimeType"

image/jpg
--Boundary_1_2089589253_1678121120116
Content-Type: text/plain
Content-Disposition: form-data; name="setCover"

true
--Boundary_1_2089589253_1678121120116
Content-Type: text/plain
Content-Disposition: form-data; name="name"

Cover.jpg
--Boundary_1_2089589253_1678121120116
Content-Type: image/jpeg
Content-Disposition: form-data; filename="Cover.jpg"; modification-date="Mon, 06 Mar 2023 16:45:19 GMT"; size=105034; name="file"

<BINARY! content>
--Boundary_1_2089589253_1678121120116--

我尝试了很多方法,包括在quarkus.io上的multipart的文档。文档的主要部分如下:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
String sendMultipartData(@MultipartForm MultipartBody data);

我还尝试了几乎所有在互联网上找到的方法,但没有一种方法有效。问题大多数时候是:文件内容不以二进制形式发送,而是以Base64形式发送。

那么,如何使用Quarkus以二进制内容发送multipart,就像以“旧”方式一样呢?我已经尝试了几周,感到非常沮丧。

以下是我最后尝试的代码:

@Path(ADD_ATTACHMENT_CARD_URL)
@POST
@javax.ws.rs.Consumes(MediaType.MULTIPART_FORM_DATA)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@ClientHeaderParam(name = "Accept", value = MediaType.APPLICATION_JSON)
@ClientHeaderParam(name = "User-Agent", value = "FawnKeeper")
public void setCover(@QueryParam("key") String key, @QueryParam("token") String token, @PathParam("id") String id, @MultipartForm MultipartBody file);

其中MultipartBody类似于以下内容(我对其进行了许多更改,首次尝试使用InputStream,就像在Quarkus文档中一样,但导致类似于“未找到转换器以将其转换为字符串”的情况):

请注意:我首先尝试在每个成员上使用@FormParam注解,但不起作用(“转换器…”)。

public class MultipartBody implements Serializable {

    @PartType(MediaType.TEXT_PLAIN)
    public String mimeType = "image/jpg";

    @PartType(MediaType.TEXT_PLAIN)
    public String setCover = "false";

    @PartType(MediaType.TEXT_PLAIN)
    public String name = "Cover.jpg";

    @PartType("image/jpeg")
    public File file;
}

我还尝试了使用Vertx客户端,但没有运气。

请注意:由于存在冲突的依赖关系,我无法使用旧代码。

如果有人能指导我正确的方向或以前在Trello上完成过这个任务,我将非常高兴。谢谢您的时间!

英文:

I'm trying to update an old Java software and port it to Quarkus with RESTEASY / Reactive / Microprofile, which adds an image to a card at Trello (Trello doc: Create attachment at card. Please note: The documentation has some lacks and cannot be taken as fully "true"). For this I have to send a multipart/form-data with some properties (name, filename, mimeType, setCover and binary file). The big point: Sending the file as BINARY.

My old software worked properly:

        String name = &quot;Cover.jpg&quot;;
        File tempImage = new File(name);
        try {
        
            Client client = ClientBuilder.newClient(new ClientConfig())
                    .register(MultiPartFeature.class);

            String mime = &quot;image/jpg&quot;;

            byte[] buffer = Files.readAllBytes(Paths.get(&quot;/PATH/&quot; + name));

            // Save temporary
            FileOutputStream fos = new FileOutputStream(tempImage);
            fos.write(buffer);
            fos.flush();
            fos.close();

            FileDataBodyPart filePart = new FileDataBodyPart(&quot;file&quot;, tempImage);

            FormDataMultiPart formDataMultiPart = new FormDataMultiPart();
            final FormDataMultiPart multipart = (FormDataMultiPart) formDataMultiPart
                    .field(&quot;mimeType&quot;, mime)
                    .field(&quot;setCover&quot;, &quot;true&quot;).field(&quot;name&quot;, name).bodyPart(filePart);


            // Add Cover
            WebTarget coverService = client.target(URI.create(&quot;https://api.trello.com/1/cards/CARD_ID/attachments&quot;));
            String coverResponse = coverService.queryParam(&quot;key&quot;, &quot;KEY&quot;).queryParam(&quot;token&quot;, &quot;TOKEN&quot;).request()
                    .header(&quot;Accept&quot;, &quot;application/json&quot;)
                    .post(Entity.entity(multipart, multipart.getMediaType()), String.class);

Which produces something like that:

1 * Sending client request on thread main
1 &gt; POST https://api.trello.com/1/cards/CARD_ID/attachments?key=KEY&amp;token=TOKEN
1 &gt; Accept: application/json
1 &gt; Content-Type: multipart/form-data
--Boundary_1_2089589253_1678121120116
Content-Type: text/plain
Content-Disposition: form-data; name=&quot;mimeType&quot;

image/jpg
--Boundary_1_2089589253_1678121120116
Content-Type: text/plain
Content-Disposition: form-data; name=&quot;setCover&quot;

true
--Boundary_1_2089589253_1678121120116
Content-Type: text/plain
Content-Disposition: form-data; name=&quot;name&quot;

Cover.jpg
--Boundary_1_2089589253_1678121120116
Content-Type: image/jpeg
Content-Disposition: form-data; filename=&quot;Cover.jpg&quot;; modification-date=&quot;Mon, 06 Mar 2023 16:45:19 GMT&quot;; size=105034; name=&quot;file&quot;

&lt;BINARY! content&gt;
--Boundary_1_2089589253_1678121120116--

I tried many things, including the docs for multipart at quarkus.io. The main point of the doc:

@POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    String sendMultipartData(@MultipartForm MultipartBody data);

Also I tried nearly everything I found at the internet, but nothing works. The problem is most of the time: The content of the file is not sent as binary, instead as Base64.

So how can I send multipart with a binary content exactly like with the "old" way, but with Quarkus? I'm trying for weeks now and am really frustrated.

Here is the last code I tried:

    @Path(ADD_ATTACHMENT_CARD_URL)
    @POST
    @javax.ws.rs.Consumes(MediaType.MULTIPART_FORM_DATA)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @ClientHeaderParam(name = &quot;Accept&quot;, value = MediaType.APPLICATION_JSON)
    @ClientHeaderParam(name = &quot;User-Agent&quot;, value = &quot;FawnKeeper&quot;)
    public void setCover(@QueryParam(&quot;key&quot;) String key, @QueryParam(&quot;token&quot;) String token, @PathParam(&quot;id&quot;) String id,@MultipartForm MultipartBody file);

Where MultipartBody is something like this (changed it a lot, first try was with InputStream like at Quarkus docs, but resulted in something like "Did not find converter to convert to String..."):

Please note: I tried first with Annotation @FormParam on every member, but did not work ("Converter...").

public class MultipartBody implements Serializable {

    @PartType(MediaType.TEXT_PLAIN)
    public String mimeType = &quot;image/jpg&quot;;

    @PartType(MediaType.TEXT_PLAIN)
    public String setCover = &quot;false&quot;;

    @PartType(MediaType.TEXT_PLAIN)
    public String name = &quot;Cover.jpg&quot;;


    @PartType(&quot;image/jpeg&quot;)
    public File file;

}

I also tried with vertx client, but had no luck...

Please note: I'm not able to use the old code because of conflicting dependencies.

I'm really happy if someone can point me to the right direction or had done this before with Trello. Thank you for your time!

答案1

得分: 2

以下是您要翻译的内容:

Got it to work by random...

For all who has similar problems, here's how I got it to work.

Create MultiDataFormOutput instance, fill it like this:

    byte[] buffer = getBufferFromFile();
    String filename = "MyFile.jpg";
    MultipartFormDataOutput dataOutput = new MultipartFormDataOutput();
    dataOutput.addFormData("mimeType", mimeType, MediaType.TEXT_PLAIN_TYPE);
    dataOutput.addFormData("setCover", "true", MediaType.TEXT_PLAIN_TYPE);
    dataOutput.addFormData("name", filename, MediaType.TEXT_PLAIN_TYPE);
    dataOutput.addFormData("file", buffer, MediaType.APPLICATION_OCTET_STREAM_TYPE, filename);

At the client interface I did it this way:

    // Just for clarification
    import javax.ws.rs.Consumes;
    import javax.ws.rs.Produces;
    import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
    import org.jboss.resteasy.annotations.providers.multipart.PartType;
    import jakarta.ws.rs.core.MediaType;
    import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;

    @Path(ADD_ATTACHMENT_CARD_URL)
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @ClientHeaderParam(name = "Accept", value = MediaType.APPLICATION_JSON)
    @ClientHeaderParam(name = "User-Agent", value = "FawnKeeper")
    public void setCover(@QueryParam("key") String key, @QueryParam("token") String token, @PathParam("id") String id, @MultipartForm MultipartFormDataOutput file);

I tried this several times before, but did not work. The body contained always just the string representation of "file" - the MultipartFormDataOutput object.

To get this working with Quarkus rest client reactive, an additional configuration is needed at application.properties/yaml:

quarkus.index-dependency.resteasy-multipart.group-id=org.jboss.resteasy
quarkus.index-dependency.resteasy-multipart.artifact-id=resteasy-multipart-provider

This is the main difference from classic rest client to reactive rest client for getting multipart to work.

Hope this helps someone!

英文:

Got it to work by random...

For all who has similar problems, here's how I got it to work.

Create MultiDataFormOutput instance, fill it like this:

    byte[] buffer = getBufferFromFile();
    String filename = &quot;MyFile.jpg&quot;;
    MultipartFormDataOutput dataOutput = new MultipartFormDataOutput();
    dataOutput.addFormData(&quot;mimeType&quot;, mimeType, MediaType.TEXT_PLAIN_TYPE);
    dataOutput.addFormData(&quot;setCover&quot;, &quot;true&quot;, MediaType.TEXT_PLAIN_TYPE);
    dataOutput.addFormData(&quot;name&quot;, filename, MediaType.TEXT_PLAIN_TYPE);
    dataOutput.addFormData(&quot;file&quot;, buffer, MediaType.APPLICATION_OCTET_STREAM_TYPE, filename);

At the client interface I did it this way:

    // Just for clarification
    import javax.ws.rs.Consumes;
    import javax.ws.rs.Produces;
    import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
    import org.jboss.resteasy.annotations.providers.multipart.PartType;
    import jakarta.ws.rs.core.MediaType;
    import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;

    @Path(ADD_ATTACHMENT_CARD_URL)
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @ClientHeaderParam(name = &quot;Accept&quot;, value = MediaType.APPLICATION_JSON)
    @ClientHeaderParam(name = &quot;User-Agent&quot;, value = &quot;FawnKeeper&quot;)
    public void setCover(@QueryParam(&quot;key&quot;) String key, @QueryParam(&quot;token&quot;) String token, @PathParam(&quot;id&quot;) String id, @MultipartForm MultipartFormDataOutput file);

I tried this several times before, but did not work. The body contained always just the string representation of "file" - the MultipartFormDataOutput object.

To get this working with Quarkus rest client reactive, an additional configuration is needed at application.properties/yaml:

quarkus.index-dependency.resteasy-multipart.group-id=org.jboss.resteasy
quarkus.index-dependency.resteasy-multipart.artifact-id=resteasy-multipart-provider

This is the main difference from classic rest client to reactive rest client for getting multipart to work.

Hope this helps someone!

huangapple
  • 本文由 发表于 2023年3月7日 15:30:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/75659077.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定