[JAVA] How to achieve file upload with Feign

1.First of all

Last time explained how to realize file download with Feign. This time I would like to explain how to realize file upload with Feign. I implemented my own Decoder in the download, but I will use this because there is a dedicated module called feign-form for uploading. .. In addition, Official page of GitHub explains as follows.

This module adds support for encoding application/x-www-form-urlencoded and multipart/form-data forms.

2. Preparation of library

feign-form is a separate module, so add it to the dependency. Please refer to Another article for the basic pom.xml when using Feign.

pom.xml


    <!-- add feign-form for multipart : start -->
    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form</artifactId>
        <version>3.3.0</version>
    </dependency>
    <!-- add feign-form for multipart : end -->

3. API interface definition

Defines the API interface. There are the following three types of argument data types that can be used for file upload data conversion (serialization).

As a sample, I would like to use three types of data this time.

UploadApi.java


package com.example.feign.demo.upload;

import java.io.File;

import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.form.FormData;

public interface UploadApi {

    @RequestLine("POST /data/upload")
    @Headers("Content-Type: multipart/form-data")
    String upload(@Param("remarks") String remarks, @Param("uploadFile") File uploadFile);

    @RequestLine("POST /data/upload")
    @Headers("Content-Type: multipart/form-data")
    String upload(@Param("remarks") String remarks, @Param("uploadFile") byte[] uploadFile);

    @RequestLine("POST /data/upload")
    @Headers("Content-Type: multipart/form-data")
    String upload(@Param("remarks") String remarks, @Param("uploadFile") FormData uploadFile);
}

** Please note that all three types do not work the same. Check the specifications on the API provider (server) side and use the appropriate one. ** **

Data type Content-Disposition:Filename Content-Type:
java.io.File ○: Set ◯: Set (automatic judgment from extension)
byte[] ×: Not set ×: Not set
Uniformapplication/octet-streamBecomes
feign.form.FormData ×: Not set ○: Set (explicitly set by the program)

4. How to use

The point is to set an instance of the feign.form.FormEncoder class in the ʻencodermethod. Others are the same as the basic usage ofFeign`.

DownloadDemo.java


package com.example.feign.demo.upload;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import feign.Feign;
import feign.Logger;
import feign.form.FormData;
import feign.form.FormEncoder;
import feign.jackson.JacksonEncoder;
import feign.okhttp.OkHttpClient;
import feign.slf4j.Slf4jLogger;

public class UploadDemo {

    public static void main(String[] args) {
        // 1. create instance of api interface with feign
        UploadApi uploadApi = Feign.builder()
                .client(new OkHttpClient())
                // use FormEncoder with JacksonEncoder
                .encoder(new FormEncoder(new JacksonEncoder()))
                // .encoder(new FormEncoder())
                .logger(new Slf4jLogger())
                .logLevel(Logger.Level.FULL)
                .target(UploadApi.class, "http://localhost:3000");

        // 2. call api
        // File
        File uploadFile = new File("C:/tmp/test.png ");
        String remarks = "I send a image file.";
        String result = uploadApi.upload(remarks, uploadFile);
        System.out.println(result);

        try (FileInputStream iStream = new FileInputStream(uploadFile);) {
            // byte[]
            byte[] uploadFileByte = new byte[(int) uploadFile.length()];
            iStream.read(uploadFileByte);
            String result2 = uploadApi.upload(remarks, uploadFileByte);
            System.out.println(result2);
            // FormData
            FormData formData = new FormData("image/png", uploadFileByte);
            String result3 = uploadApi.upload(remarks, formData);
            System.out.println(result3);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. Explain the difference between the three types with the implementation code (bonus)

The implementation code of feign-form is open to the public, so you can see the difference between the three types of operations. FormData can be solved by having a field of filename, but it is a mystery why it is not retained. (Although it retains contentType)

5.1. File

https://github.com/OpenFeign/feign-form/blob/master/feign-form/src/main/java/feign/form/multipart/SingleFileWriter.java

SingleFileWriter.Where the writeFileMetadata method of the parent class (AbstractWriter) is called in java


writeFileMetadata(output, key, file.getName(), null);

https://github.com/OpenFeign/feign-form/blob/master/feign-form/src/main/java/feign/form/multipart/AbstractWriter.java

AbstractWriter.java writeFileMetadata method


  @SneakyThrows
  protected void writeFileMetadata (Output output, String name, String fileName, String contentType) {
    val contentDesposition = new StringBuilder()
        .append("Content-Disposition: form-data; name=\"").append(name).append("\"; ")
        .append("filename=\"").append(fileName).append("\"")
        .toString();

    String fileContentType = contentType;
    if (fileContentType == null) {
      if (fileName != null) {
        fileContentType = URLConnection.guessContentTypeFromName(fileName);
      }
      if (fileContentType == null) {
        fileContentType = "application/octet-stream";
      }
    }

    val string = new StringBuilder()
        .append(contentDesposition).append(CRLF)
        .append("Content-Type: ").append(fileContentType).append(CRLF)
        .append("Content-Transfer-Encoding: binary").append(CRLF)
        .append(CRLF)
        .toString();

    output.write(string);
  }

In other words, in the case of File, it will be as follows.

5.2. byte[]

https://github.com/OpenFeign/feign-form/blob/master/feign-form/src/main/java/feign/form/multipart/ByteArrayWriter.java

ByteArrayWriter.Where the writeFileMetadata method of the parent class (AbstractWriter) is called in java


writeFileMetadata(output, key, null, null);

In other words, in the case of byte [], it will be as follows.

5.3. FormData

https://github.com/OpenFeign/feign-form/blob/master/feign-form/src/main/java/feign/form/multipart/FormDataWriter.java

FormDataWriter.Where the writeFileMetadata method of the parent class (AbstractWriter) is called in java


writeFileMetadata(output, key, null, formData.getContentType());

In other words, in the case of FormData, it will be as follows.

6. Finally

This time, I explained how to realize file upload with Feign. I think it was easier than downloading because of the modules provided.

Recommended Posts

How to achieve file upload with Feign
How to achieve file download with Feign
How to realize huge file upload with TERASOLUNA 5.x (= Spring MVC)
How to realize huge file upload with Rest Template of Spring
I tried to implement file upload with Spring MVC
iOS: File upload with SFTP
How to number (number) with html.erb
How to update with activerecord-import
How to test file upload screen in Spring + Selenium
How to request a CSV file as JSON with jMeter
How to decompile apk file to java source code with MAC
How to set up a proxy with authentication in Feign
How to change the file name with Xcode (Refactor Rename)
How to get started with slim
How to enclose any character with "~"
How to convert erb file to haml
How to achieve paiza rank D
How to get along with Rails
[Beginner] How to delete NO FILE
How to start Camunda with Docker
How to make a jar file with no dependencies in Maven
How to load a Spring upload file and view its contents
Automatic file upload with old Ruby gem What to do with Watir
How to deal with the event that Committee :: InvalidRequest occurs in committee during Rspec file upload test
[Xcode] How to add a README.md file
How to crop an image with libGDX
How to adjustTextPosition with iOS Keyboard Extension
[Java] How to use the File class
How to compile Java with VsCode & Ant
How to delete the wrong migration file
[Rails] How to upload images using Carrierwave
[Java] How to compare with equals method
[Android] How to deal with dark themes
How to use BootStrap with Play Framework
[Rails] How to use rails console with docker
[Note] How to get started with Rspec
Upload Rails app image file to S3
How to add jar file in ScalaIDE
How to do API-based control with cancancan
How to implement TextInputLayout with validation function
How to handle sign-in errors with devise
How to delete data with foreign key
How to test private scope with JUnit
How to monitor nginx with docker-compose with datadog
How to split Spring Boot message file
How to run Blazor (C #) with Docker
How to build Rails 6 environment with Docker
How to start a Docker container with a volume mounted in a batch file
How to download Oracle JDK 8 rpm with curl
[Java] How to test for null with JUnit
How to mock each case with Mockito 1x
[Rails] How to read the XML file uploaded from the screen with Hash type
[Ruby] How to convert CSV file to Yaml (Yml)
How to mock each case with PowerMock + Mockito1x
How to test interrupts during Thread.sleep with JUnit
Send and process form data to Servlet with FormData object and Commons File Upload
How to dump from database (DB) to seeds file
How to use built-in h2db with spring boot
How to search multiple columns with gem ransack
How to deploy
[Swift] How to link the app with Firebase