Handle multipart/form-data with Quarkus
Asked Answered
K

3

8

I'm facing an issue, I can't get my form in my resources, the variables are always null

My Resource :

    @POST
    @Path("/upload-logo")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    public String uploadLogo (@MultipartForm LogoMultipartForm logoMultipartForm) throws IOException {
        return this.companyService.uploadLogo(username, logoMultipartForm.logo);

    }

The form model

public class LogoMultipartForm {

    @FormParam("logo")
    public byte[] logo;

    @FormParam("filename")
    @PartType("text/plain")
    public String fileName;
}

My Fetch request :

  uploadLogo: async (file: File) => {
    const form = new FormData();
    form.append("logo", file, "logo.png");
    form.append("filename", "test");

    const { query, abort } = HttpClient.POST(`${COMPANY_URL}/upload-logo`, form);
    let promise = query
      .then((res: any) => {
        console.log("Response", res);
        if (res.status === 200) {
          return res.text();
        } else {
          throw res;
        }
      })
      .then((url: any) => url);

    promise.cancel = abort;

    return promise;
  },

And my HttpClient :

  POST: function (url: string, body: any, config?: any) {
    const controller = new AbortController();
    const signal = controller.signal;
    return { query: fetch(url, { signal, method: "POST", body, ...config }) as any, abort: () => controller.abort() };
  },

To be sure I was testing with a proxy and the request is effectively good : The variable fileName and logo is always null.

This is my pom.xml :

  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-graphql</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-flyway</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-hibernate-orm-panache</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-reactive-pg-client</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-qute</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-oidc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-rest-client</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-multipart</artifactId>
    </dependency>

  </dependencies>

I don't see where is the problem.

I have already seen the official resteasy documentation for Multipart and don't see where is the problem. I have already Tested with MultipartFormDataInput and all parts are empty.

Thanks in advance for your help ! :)

Kunzite answered 7/11, 2020 at 17:11 Comment(0)
D
3

You are missing the dependency:

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-multipart-provider</artifactId>
</dependency>

Be sure that your @PartType("text/plain") inherits from org.jboss.resteasy package! As well as the @MultipartForm (import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;)

There is a good example how it works: Quarkus tutorial. Look at the packages!

Duncan answered 12/11, 2020 at 9:47 Comment(2)
Quarkus provide an extension quarkus-reasteasy-multipart so what is the differences ?Kunzite
quarkus-reasteasy-multipart set default charset encoding to UTF-8 and default media type to text/plain, and it is transitively depending on resteasy-multipart-provider.Previdi
B
1

Client side I'd used Angular17 and Backend is Quarkus.

I was facing similar kind of issue when I was hitting via http post call (the formData, which I was passing from client to API it was always null)

For resolving this I set header as below; Click here to see image

Bolte answered 28/5 at 13:14 Comment(1)
Thank you, this worked for me. Even though I had setted the header with a multipart content type. 👍Lili
R
0

Spent hours on figure out how to solve multipart data sending three a client to client

Hope so this example will help You

RestClient

@RegisterRestClient(configKey = "ted-api")
@RegisterClientHeaders(ClientHeaderFactory.class)
@RegisterProvider(value = ExceptionMapper.class)
@ApplicationScoped
public interface TedClient {
  
  @POST
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  @Path("/notices/submit")
  Response sendData(@MultipartForm TedBody data);
}

Body

@Getter
@Setter
@NoArgsConstructor
public class TedBody {
  @FormParam("notice")
  @PartFilename("notice-filename.xml") //partFileName is necessary for multipart mainly many developers missing it
  @PartType(MediaType.TEXT_XML)
  private byte[] notice;

  @FormParam("metadata")
  @PartType(MediaType.APPLICATION_JSON)
  private AuthorData AuthorData;

  public TedBody(byte[] notice) {
    this.notice = notice;
    this.authorData = new Author();
  }

in case if Your systems need to retrieve a multipart file then example may be easier Rest

  @POST
  @Path("/upload")
  @Consumes(MULTIPART_FORM_DATA)
  @Produces(APPLICATION_JSON)
  public Response saveMultipartNoticeContainer(
      @MultipartForm @Valid RequestDto requestDto) {
    Resnponse noticeDto = noticeService.save(requestDto);
    return Response.accepted().entity(noticeDto).build();
  }
import org.jboss.resteasy.annotations.providers.multipart.PartType;

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class NoticeRequestDto {

  @FormParam("notice")
  @PartType(MediaType.APPLICATION_OCTET_STREAM)
  private InputStream data;
}

for us works on Quarkus - 2.15.3.Final

Ronaronal answered 13/1, 2023 at 10:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.