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)
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
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".
setConnectTimeout
: Legt das Verbindungszeitlimit (Einheit ist Millisekunde) der URL-Verbindung festsetReadTimeout
: Legt das Lesezeitlimit (Einheit ist Millisekunde) der URL-Verbindung festsetChunkSize
: Legt die Blockgröße (Einheit ist Byte) fest, wenn Daten in den HTTP-Anforderungskörper geschrieben werdensetBufferRequestBody
: Legt fest, ob die Anforderungsfactory intern puffert ( true: default
oder false
)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
.
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