Laden Sie eine Datei vom Spring Boot Rest Service herunter


78

Ich versuche, eine Datei von einem Spring Boot Rest-Dienst herunterzuladen.

@RequestMapping(path="/downloadFile",method=RequestMethod.GET)
    @Consumes(MediaType.APPLICATION_JSON_VALUE)
    public  ResponseEntity<InputStreamReader> downloadDocument(
                String acquistionId,
                String fileType,
                Integer expressVfId) throws IOException {
        File file2Upload = new File("C:\\Users\\admin\\Desktop\\bkp\\1.rtf");
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        InputStreamReader i = new InputStreamReader(new FileInputStream(file2Upload));
        System.out.println("The length of the file is : "+file2Upload.length());

        return ResponseEntity.ok().headers(headers).contentLength(file2Upload.length())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(i);
        }

Wenn ich versucht habe, die Datei vom Browser herunterzuladen, wird der Download gestartet, schlägt jedoch immer fehl. Stimmt etwas mit dem Dienst nicht, der dazu führt, dass der Download fehlschlägt?

Antworten:


148

Option 1 mit einer InputStreamResource

Ressourcenimplementierung für einen bestimmten InputStream .

Sollte nur verwendet werden, wenn keine andere spezifische Ressourcenimplementierung anwendbar ist. Bevorzugen Sie insbesondere ByteArrayResource oder eine der dateibasierten Ressourcenimplementierungen, sofern dies möglich ist.

@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(String param) throws IOException {

    // ...

    InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

    return ResponseEntity.ok()
            .headers(headers)
            .contentLength(file.length())
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resource);
}

Option2, wie in der Dokumentation der InputStreamResource vorgeschlagen - Verwendung einer ByteArrayResource :

@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(String param) throws IOException {

    // ...

    Path path = Paths.get(file.getAbsolutePath());
    ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));

    return ResponseEntity.ok()
            .headers(headers)
            .contentLength(file.length())
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resource);
}

2
Ich versuche es für das Word-Dokument .doc-Format zu tun, aber während des Herunterladens ist das Format weg und die Datei wird ohne Dateierweiterung heruntergeladen und der Dateiname ist eine Antwort beim Herunterladen. Irgendein Vorschlag?
Tulsi Jain

17
@ TulsiJain hinzufügen die Content-Disposition HttpHeader: HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=myDoc.docx");
Fateddy

7
Nur für den Fall, dass Sie das Pech haben, einfach Spring anstelle von Spring Boot zu verwenden, müssen Sie sicherstellen, dass eine Instanz von ResourceHttpMessageConverterzu Ihrer Liste der HttpMessageConverters hinzugefügt wird. Erstellen Sie eine @ConfigurationKlasse, die erweitert WebMvcConfigurerAdapter, implementieren Sie die configureMessageConverters () -Methode und fügen Sie eine converters.add(new ResourceHttpMessageConverter());Zeile hinzu
ashario

4
Fragen: Option 1 scheint den Stream nicht zu schließen. Wo ist die Magie? Option 2 scheint die gesamte Datei vor dem Senden in den Speicher zu laden. Richtig? Alternativen? DANKE!
Eventhorizon

20

Der folgende Beispielcode hat für mich funktioniert und könnte jemandem helfen.

import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@RequestMapping("/app")
public class ImageResource {

    private static final String EXTENSION = ".jpg";
    private static final String SERVER_LOCATION = "/server/images";

    @RequestMapping(path = "/download", method = RequestMethod.GET)
    public ResponseEntity<Resource> download(@RequestParam("image") String image) throws IOException {
        File file = new File(SERVER_LOCATION + File.separator + image + EXTENSION);

        HttpHeaders header = new HttpHeaders();
        header.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=img.jpg");
        header.add("Cache-Control", "no-cache, no-store, must-revalidate");
        header.add("Pragma", "no-cache");
        header.add("Expires", "0");

        Path path = Paths.get(file.getAbsolutePath());
        ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));

        return ResponseEntity.ok()
                .headers(header)
                .contentLength(file.length())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(resource);
    }

}

4

Ich würde vorschlagen, einen StreamingResponseBody zu verwenden, da die Anwendung damit direkt in die Antwort (OutputStream) schreiben kann, ohne den Servlet-Container-Thread aufzuhalten. Dies ist ein guter Ansatz, wenn Sie eine sehr große Datei herunterladen.

@GetMapping("download")
public StreamingResponseBody downloadFile(HttpServletResponse response, @PathVariable Long fileId) {

    FileInfo fileInfo = fileService.findFileInfo(fileId);
    response.setContentType(fileInfo.getContentType());
    response.setHeader(
        HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + fileInfo.getFilename() + "\"");

    return outputStream -> {
        int bytesRead;
        byte[] buffer = new byte[BUFFER_SIZE];
        InputStream inputStream = fileInfo.getInputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
    };
}

Ps.: Bei Verwendung von StreamingResponseBody wird dringend empfohlen, den in Spring MVC verwendeten TaskExecutor für die Ausführung asynchroner Anforderungen zu konfigurieren. TaskExecutor ist eine Schnittstelle, die die Ausführung eines Runnable abstrahiert.

Weitere Informationen: https://medium.com/swlh/streaming-data-with-spring-boot-restful-web-service-87522511c071


1
Ich konnte keine der ResponseEntity<Resource>Lösungen zum Laufen bringen. Diese Lösung hat sofort StreamingResponseBodyfunktioniert. DANKE!
t0r0X

2

Ich möchte einen einfachen Ansatz zum Herunterladen von Dateien mit JavaScript (ES6), React und einem Spring Boot- Backend teilen :

  1. Spring Boot Rest Controller

Ressource von org.springframework.core.io.Resource

    @SneakyThrows
    @GetMapping("/files/{filename:.+}/{extraVariable}")
    @ResponseBody
    public ResponseEntity<Resource> serveFile(@PathVariable String filename, @PathVariable String extraVariable) {

        Resource file = storageService.loadAsResource(filename, extraVariable);
        return ResponseEntity.ok()
               .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
               .body(file);
    }
  1. Reagieren Sie, API-Aufruf mit AXIOS

Setzen Sie den responseType auf arraybuffer , um den in der Antwort enthaltenen Datentyp anzugeben.

export const DownloadFile = (filename, extraVariable) => {
let url = 'http://localhost:8080/files/' + filename + '/' + extraVariable;
return axios.get(url, { responseType: 'arraybuffer' }).then((response) => {
    return response;
})};

Letzter Schritt> Herunterladen
mit Hilfe von js-file-download Sie können den Browser auslösen, um Daten in einer Datei zu speichern, als ob sie heruntergeladen worden wären.

DownloadFile('filename.extension', 'extraVariable').then(
(response) => {
    fileDownload(response.data, filename);
}
, (error) => {
    // ERROR 
});


0
    @GetMapping("/downloadfile/{productId}/{fileName}")
public ResponseEntity<Resource> downloadFile(@PathVariable(value = "productId") String productId,
        @PathVariable String fileName, HttpServletRequest request) {
    // Load file as Resource
    Resource resource;

    String fileBasePath = "C:\\Users\\v_fzhang\\mobileid\\src\\main\\resources\\data\\Filesdown\\" + productId
            + "\\";
    Path path = Paths.get(fileBasePath + fileName);
    try {
        resource = new UrlResource(path.toUri());
    } catch (MalformedURLException e) {
        e.printStackTrace();
        return null;
    }

    // Try to determine file's content type
    String contentType = null;
    try {
        contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
    } catch (IOException ex) {
        System.out.println("Could not determine file type.");
    }

    // Fallback to the default content type if type could not be determined
    if (contentType == null) {
        contentType = "application/octet-stream";
    }

    return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
            .body(resource);
}

Verwenden Sie zum Testen den Postboten

http: // localhost: 8080 / api / downloadfile / GDD / 1.zip


0

Die Verwendung von Apache IO könnte eine weitere Option zum Kopieren des Streams sein

@RequestMapping(path = "/file/{fileId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> downloadFile(@PathVariable(value="fileId") String fileId,HttpServletResponse response) throws Exception {

    InputStream yourInputStream = ...
    IOUtils.copy(yourInputStream, response.getOutputStream());
    response.flushBuffer();
    return ResponseEntity.ok().build();
}

Maven-Abhängigkeit

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
    </dependency>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.