завантажити файл зі служби відпочинку для завантаження Spring


83

Я намагаюся завантажити файл із служби Spring rest rest.

@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);
        }

Коли я намагався завантажити файл із браузера, він починає завантаження, але завжди не вдається. Чи є щось не так із службою, яка спричиняє невдале завантаження?

Відповіді:


154

Варіант 1 із використанням InputStreamResource

Реалізація ресурсу для даного InputStream .

Використовувати лише в тому випадку, якщо жодна інша конкретна реалізація ресурсів не застосовується Зокрема, віддайте перевагу ByteArrayResource або будь-якій реалізації файлових ресурсів, де це можливо.

@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);
}

Варіант 2, як пропонується документацією InputStreamResource - використання 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
Я намагаюся зробити це для формату word .doc документа, але під час завантаження формат зникає, і файл завантажується без розширення файлу, а ім'я файлу є відповіддю під час завантаження. Будь-яка пропозиція?
Tulsi Jain

18
@TulsiJain додає HttpHeader Content-Disposition: HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=myDoc.docx");
fateddy

7
на той випадок, якщо вам не пощастило використовувати звичайну Spring замість Spring Boot, вам потрібно переконатися, що екземпляр ResourceHttpMessageConverterдодано до вашого списку HttpMessageConverters. Створіть @Configurationклас, що розширюється WebMvcConfigurerAdapter, converters.add(new ResourceHttpMessageConverter());
реалізуйте

4
Запитання: Здається, варіант 1 не закриває потік. Де магія? Здається, варіант 2 завантажує повний файл у пам’ять перед надсиланням. Правильно? Альтернативи? ДЯКУЮ!
eventhorizon

22

Наведений нижче зразок коду спрацював для мене і міг комусь допомогти.

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);
    }

}

6

Я б запропонував використовувати StreamingResponseBody, оскільки за допомогою нього програма може писати безпосередньо у відповідь (OutputStream), не затримуючи потоку контейнера сервлетів. Це хороший підхід, якщо ви завантажуєте дуже великий файл.

@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 .: При використанні StreamingResponseBody настійно рекомендується налаштувати TaskExecutor, який використовується у Spring MVC, для виконання асинхронних запитів. TaskExecutor - це інтерфейс, який абстрагується від виконання Runnable.

Більше інформації: https://medium.com/swlh/streaming-data-with-spring-boot-restful-web-service-87522511c071


2

Я хочу поділитися простим підходом для завантаження файлів з JavaScript (ES6), React та бекендом Spring Boot :

  1. Весняний контролер відпочинку

Ресурс від 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. React, виклик API за допомогою AXIOS

Встановіть для responseType значення масиву буферів, щоб вказати тип даних, що містяться у відповіді.

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

Заключний крок> завантаження
за допомогою js-file-download ви можете запустити браузер для збереження даних у файл так, ніби вони були завантажені.

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

2

Якщо вам потрібно завантажити величезний файл із файлової системи сервера, тоді ByteArrayResource може зайняти весь простір Java. У цьому випадку ви можете використовувати FileSystemResource


1
    @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);
}

Щоб перевірити це, використовуйте листоношу

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


0

використання Apache IO може бути ще одним варіантом копіювання потоку

@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

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
    </dependency>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.