[JAVA] So erzielen Sie mit Rest Template of Spring einen großen Datei-Upload

1. Zuallererst

Dieses Mal werde ich erklären, wie man eine große Datei mit "RestTemplate" von "Spring Framework" hochlädt. Der Punkt ist, "RequestCallback" zu verwenden. Informationen zur serverseitigen Implementierung des Hochladens großer Dateien finden Sie unter "So realisieren Sie das Hochladen großer Dateien mit TERASOLUNA 5.x (= Spring MVC)".

(Punkt)

2. Quellcode

LargeFileRequestCallback.java


package todo.app.largefile;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.web.client.RequestCallback;

//★ Punkt 1
public class LargeFileRequestCallback implements RequestCallback {

    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory
            .getLogger(LargeFileRequestCallback.class);

    /**
     *★ Punkt 2
     * buffer size 1MB
     */
    private static final int BUFFER_SIZE = 1 * 1024 * 1024;

    /**
     *★ Punkt 3
     *Dateiinformationen
     */
    private final FileInfo fileInfo;

    /**
     *★ Punkt 3
     *Konstrukteur
     * @param fileInfo Dateiinformationen
     */
    public LargeFileRequestCallback(FileInfo fileInfo) {
        this.fileInfo = fileInfo;
    }

    /**
     *★ Punkt 4
     * @see org.springframework.web.client.RequestCallback#doWithRequest(org.springframework.http.client.ClientHttpRequest)
     */
    @Override
    public void doWithRequest(ClientHttpRequest request) throws IOException {

        LOGGER.debug("doWithRequest : start");
        //★ Punkt 5
        // 1. set Content-type
        request.getHeaders().add("Content-type", fileInfo.getContentType());
        request.getHeaders().add("Content-Length",
                Long.toString(fileInfo.getContentLength()));
        request.getHeaders().add("X-SHA1-CheckSum", fileInfo.getCheckSum());
        request.getHeaders().add("X-FILE-NAME", fileInfo.getDownloadFileName());

        //★ Punkt 6
        // 2. copy contents with buffering
        try (InputStream input = new BufferedInputStream(
                new FileInputStream(fileInfo.getFilePath()));
                OutputStream out = request.getBody();) {
            byte[] buffer = new byte[BUFFER_SIZE];
            long total = 0;
            int len = 0;
            while ((len = input.read(buffer)) != -1) {
                out.write(buffer, 0, len);
                out.flush();
                total = total + len;
                // LOGGER.debug("writed : " + total);
            }
        }

        LOGGER.debug("doWithRequest : end");
    }

}

** ★ Punkt 1 ** Definieren Sie eine RequestCallback-Klasse, die die org.springframework.web.client.RequestCallback-Schnittstelle implementiert. Diese Klasse kann den Anforderungsheader bearbeiten, wenn sie eine HTTP-Anforderung für "RestTemplate" ausgibt, und Daten in den Anforderungskörper schreiben. Dieses Mal werden die Dateidaten in den Anforderungskörper geschrieben. Dieses Mal wurde es als separate Klasse definiert, damit es von anderen verwendet werden kann. Es ist jedoch auch möglich, es als (1) Pfeiloperator (Lambda-Ausdruck) und (2) innere Klasse (anonyme Klasse) zu schreiben, in der es verwendet wird.

** ★ Punkt 2 ** Definiert die Puffergröße beim Schreiben von Dateidaten. Diesmal war es 1 MB.

** ★ Punkt 3 ** Die hochzuladenden Dateiinformationen sind als Konstruktorargument mit der Klasse "RequestCallback" verknüpft.

** ★ Punkt 4 ** Dies ist der Punkt dieses Artikels. Implementieren Sie den Prozess des Schreibens der Daten der Upload-Datei in die HTTP-Anforderung, indem Sie die Methode "doWithRequest" überschreiben. Das als Argument übergebene ClientHttpRequest-Objekt ist die von RestTemplate ausgegebene HTTP-Anforderung. Bearbeiten Sie dies direkt, um den Anforderungsheader festzulegen oder Daten in den Anforderungskörper zu schreiben.

** ★ Punkt 5 ** Rufen Sie beim Festlegen des Anforderungsheaders das Objekt "HttpHeaders" mit der Methode "getHeaders" ab und fügen Sie es mit der Methode "add" hinzu.

** ★ Punkt 6 ** Schreiben Sie, um BODY anzufordern, während Sie Dateidaten puffern. Auf den Anforderungs-BODY wird als Ausgabestream zugegriffen, indem die Methode "getBody" des Objekts "ClientHttpRequest" aufgerufen wird.

RestTemplateCallbackExtend.java


package todo.app.largefile;

import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

//★ Punkt 7
public class RestTemplateCallbackExtend extends RestTemplate {

    public RestTemplateCallbackExtend() {
        super();
    }

    /**
     *★ Punkt 8
     * Extend the postForEntity method so that callback can be used
     * @param url the URL
     * @param responseType the type of the return value
     * @param requestCallback object that prepares the request
     * @return the converted object
     */
    public <T> ResponseEntity<T> postForEntityWithCallback(String url,
            Class<T> responseType, RequestCallback requestCallback) {
        
        // 1. create ResponseExtractor object
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(
                responseType);
        
        // 2. send request using RequestCallback and default ResponseExtractor
        return super.execute(url, HttpMethod.POST, requestCallback,
                responseExtractor);
    }
}

** ★ Punkt 7 ** Standardmäßig verfügt "RestTemplate" nur über drei "Ausführungs" -Methoden, die "RequestCallback" verwenden. In diesem Fall müssen Sie auch "ResponseExtractor" angeben. Ich habe "RequestCallback" implementiert, aber "ResponseExtractor" möchte die Standardfunktionalität nutzen, daher werde ich "RestTemplate" ein wenig erweitern.

(Referenz) Da es schwierig war, "ResponseExtractor <ResponseEntity >" von "ResponseEntity " zu erstellen, habe ich diesmal "RestTemplate" erweitert. Wenn Sie dieses ResponseExtractor-Objekt problemlos erstellen können, sollten Sie die Methode execute von RestTemplate direkt aufrufen.

Liste der Methoden, die RequestCallback als Argument in RestTemplate verwenden


public <T> T execute(String url,
                     HttpMethod method,
                     RequestCallback requestCallback,
                     ResponseExtractor<T> responseExtractor,
                     Object... urlVariables)
              throws RestClientException

public <T> T execute(String url,
                     HttpMethod method,
                     RequestCallback requestCallback,
                     ResponseExtractor<T> responseExtractor,
                     Map<String,?> urlVariables)
              throws RestClientException

public <T> T execute(URI url,
                     HttpMethod method,
                     RequestCallback requestCallback,
                     ResponseExtractor<T> responseExtractor)
              throws RestClientException

** ★ Punkt 8 ** Dieses Mal wollte ich eine "postForEntity" -Methode, die "RequestCallback" als Argument in "POST" angeben kann, also habe ich die "postForEntityWithCallback" -Methode hinzugefügt. Rufen Sie die protected`` responseEntityExtractor -Methode auf, um den Standard ResponseExtractor zu verwenden, und RequestCallback verwendet die durch das Argument angegebene Methode, um die execute -Methode auszuführen.

FileUploadControllerTest.java


package todo.app.largefile;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.HttpClientErrorException;

public class FileUploadControllerTest {

    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory
            .getLogger(FileUploadControllerTest.class);

    @Test
    public void test01() {
        //★ Punkt 9
        // 1. prepared data
        String targetUrl = "http://localhost:8090/todo-rest/upload/chunked";
        FileInfo fileInfo = getFileInfo();

        //★ Punkt 10
        // 2. create RestTemplate object
        RestTemplateCallbackExtend restTemplate = new RestTemplateCallbackExtend();

        //★ Punkt 11
        // 3. setting SimpleClientHttpRequestFactory for timeout, streaming
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setBufferRequestBody(false);
        requestFactory.setConnectTimeout(1000 * 120);
        requestFactory.setReadTimeout(1000 * 120);
        requestFactory.setChunkSize(1 * 1024 * 1024);
        restTemplate.setRequestFactory(requestFactory);

        try {
            //★ Punkt 12
            // 4. send request using RequestCallback
            ResponseEntity<String> responseEntity = restTemplate
                    .postForEntityWithCallback(targetUrl, String.class,
                            new LargeFileRequestCallback(fileInfo));

            //★ Punkt 13
            // 5. assert
            assertThat(responseEntity.getStatusCode(), is(HttpStatus.CREATED));
            assertThat(responseEntity.getBody(), is("success!"));

        } catch (HttpClientErrorException e) {
            LOGGER.error(e.getMessage()); //Nur "403 Verboten" ist enthalten.
            LOGGER.error(e.getResponseBodyAsString());
            throw e;
        }

    }

    /**
     *★ Punkt 9
     *Holen Sie sich Dateiinformationen zum Testen
     * @Dateiinformationen zurückgeben
     */
    private FileInfo getFileInfo() {
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFilePath("C:/tmp/spring-tool-suite-3.8.2.RELEASE-e4.6.1-win32-x86_64.zip");
        fileInfo.setContentType("application/zip");
        fileInfo.setCheckSum("ff1a62872fb62edb52c1e040fdf9c720e051f9e8");
        fileInfo.setContentLength(418945672);
        fileInfo.setDownloadFileName("STS3.8.2.zip");
        return fileInfo;
    }
}

** ★ Punkt 9 ** Bereiten Sie die zu sendenden Daten vor. Dieses Mal habe ich beschlossen, die STS-Zip-Datei (etwas weniger als 400 MB) hochzuladen, die ich hatte. Die Prüfsumme (Hash-Wert) wurde in "So erhalten Sie den Hash-Wert (Prüfsumme) einer großen Datei in Python" berechnet.

** ★ Punkt 10 ** Erstellen Sie eine Instanz der an Punkt 7 definierten Klasse "RestTemplateCallbackExtend" anstelle der "RestTemplate".

** ★ Punkt 11 ** Beim Hochladen einer großen Datei müssen die Auswirkungen von Zeitüberschreitung, Pufferdatengröße usw. berücksichtigt werden. Konfigurieren Sie mit der Klasse "org.springframework.http.client.SimpleClientHttpRequestFactory" und aktivieren Sie sie mit der Methode "setRequestFactory" der "RestTemplate".

Wie unten erläutert, senden wir diesmal eine Anfrage mit einer großen Datengröße. Setzen Sie also "false".

Default is true. When sending large amounts of data via POST or PUT, it is recommended to change this property to false, so as not to run out of memory.

** ★ Punkt 12 ** ★ Rufen Sie die in Punkt 8 implementierte Methode "postForEntityWithCallback" auf. Geben Sie zu diesem Zeitpunkt das Objekt "LargeFileRequestCallback" als Argument an. Jetzt werden die Dateidaten mit dem diesmal implementierten "RequestCallback" in den Anforderungskörper geschrieben.

** ★ Punkt 13 ** Nach dem Ausführen der HTTP-Anforderung entspricht diese einer normalen "RestTemplate". Verwenden Sie die Methode "getStatusCode", um auf den HTTP-Antwortstatuscode zuzugreifen, und die Methode "getBody", um auf den HTTP-Antwort-BODY zuzugreifen. Wenn "HttpClientErrorException" auftritt, erhält die Methode "getMessage" eine Zusammenfassung der Ausnahme. Wenn Sie weitere Details benötigen, überprüfen Sie die Antwort BODY in Ausnahmefällen mit der Methode getResponseBodyAsString.

3. Schließlich

Dieses Mal habe ich erklärt, wie man eine große Datei mit "RestTemplate" und "RequestCallback" hochlädt. Wie ich diesmal herausgefunden habe, war es etwas unpraktisch, dass es keine Methode gibt, die "RequestCallback" unabhängig in "RestTemplate" angeben kann (einige sind mit "ResponseExtractor" festgelegt).

Recommended Posts

So erzielen Sie mit Rest Template of Spring einen großen Datei-Upload
So realisieren Sie mit TERASOLUNA 5.x (= Spring MVC) das Hochladen großer Dateien
So erreichen Sie das Hochladen von Dateien mit Feign
Ich habe versucht, das Hochladen von Dateien mit Spring MVC zu implementieren
Datei-Upload mit Spring Boot
So testen Sie den Bildschirm zum Hochladen von Dateien mit Spring + Selenium
So erstellen Sie ein Excel-Formular mithilfe einer Vorlagendatei mit Spring MVC
Booten nach Umgebung mit Spring Boot of Maven
Schneiden Sie SQL in die Eigenschaftendatei mit jdbcTemplate von Spring Boot aus
Lassen Sie uns herausfinden, wie Sie mit Request Body mit der REST-API von Spring Boot empfangen können
So laden Sie eine Spring-Upload-Datei und zeigen ihren Inhalt an
So erreichen Sie den Dateidownload mit Feign
So teilen Sie eine Spring Boot-Nachrichtendatei
So lesen Sie Request Body mit Spring Boot + Spring Security mehrmals
Ich möchte Bilder mit REST Controller von Java und Spring anzeigen!
So greifen Sie mit der TCP-Funktion von Spring Integration direkt auf Socket zu
Verwendung von MyBatis2 (iBatis) mit Spring Boot 1.4 (Spring 4)
Verwendung des eingebauten h2db mit Federstiefel
So binden Sie mit einer Eigenschaftendatei in Spring Boot
[Spring Boot] So verweisen Sie auf die Eigenschaftendatei
So legen Sie Umgebungsvariablen in der Eigenschaftendatei der Spring-Boot-Anwendung fest
Wenn Sie SQLite mit VSCode verwenden, verwenden Sie die Erweiterung (wie die Binärdatei von sqlite3 angezeigt wird)
[Java] So lassen Sie die Federkonstruktorinjektion mit Lombok weg
Umgang mit Keine Vorlage für interaktive Anfragen
Verwendung von CommandLineRunner im Spring Batch von Spring Boot
Datei-Upload mit Spring Boot (keine mehrteilige Datei verwenden)
So kehren Sie die Kompilierung der Apk-Datei in Java-Quellcode mit MAC um
Verwendung des mit Tensorflow 2.0 trainierten Modells mit Kotlin / Java
[Einfach] So formatieren Sie Ruby erb-Dateien automatisch mit vsCode
So verschieben Sie eine andere Klasse mit einer Schaltflächenaktion einer anderen Klasse.
So ändern Sie den Inhalt der JAR-Datei, ohne sie zu dekomprimieren
Umgang mit verschiedenen Versionen von rbenv und Ruby
Wie man mit html.erb nummeriert (nummeriert)
So aktualisieren Sie mit activerecord-import
Umgang mit dem Ereignis, dass Committee :: InvalidRequest während des Rspec-Datei-Upload-Tests im Committee auftritt
So erstellen Sie eine JAR-Datei ohne Abhängigkeiten in Maven
Ich habe versucht, mithilfe von JDBC Template mit Spring MVC eine Verbindung zu MySQL herzustellen
[Mit Beispielcode] Grundlagen von Spring JDBC mit der Blog-App gelernt
So öffnen Sie eine Skriptdatei von Ubuntu mit VS-Code
[chown] So ändern Sie den Eigentümer einer Datei oder eines Verzeichnisses
Überschreiben Sie den gleichnamigen Upload mit dem BOX SDK (Java).
[Anfänger] Versuchen Sie, die REST-API für die Todo-App mit Spring Boot zu schreiben
Automatischer Datei-Upload mit altem Ruby-Edelstein Was tun mit Watir?
Wie man Lombok im Frühling benutzt
Feder mit Kotorin --4 REST API Design
Wie fange ich mit schlank an?
Hinweise zur Verwendung von Spring Data JDBC
Wie man ein Zeichen mit "~" einschließt
[So installieren Sie Spring Data Jpa]
So stellen Sie Spring Boot + PostgreSQL ein
So konvertieren Sie erb-Datei in haml
Verwendung von setDefaultCloseOperation () von JFrame
Implementieren Sie den Dateidownload mit Spring MVC
Wie man mssql-tools mit alpine benutzt
Verwendung von ModelMapper (Spring Boot)
So starten Sie Camunda mit Docker
So wenden Sie Thymeleaf-Änderungen sofort mit #Spring Boot + maven auf den Browser an
So dekorieren Sie CSS auf Rails6 form_with (Hilfs-) Optionsfeldern