Spring REST - создать .zip файл и отправить его клиенту
Я хочу создать ZIP файл, содержащий мои zipped файлы, которые я получаю из бэкэнд, а затем отправить этот файл пользователю. В течение 2 дней я искал ответ и не мог найти правильное решение, может быть, вы можете мне помочь:)
На данный момент код выглядит следующим образом: (я знаю, что я не должен делать все это в контроллере spring, но не заботясь об этом, он просто предназначен для тестирования, чтобы найти способ заставить его работать)
@RequestMapping(value = "/zip")
public byte[] zipFiles(HttpServletResponse response) throws IOException{
//setting headers
response.setContentType("application/zip");
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader("Content-Disposition", "attachment; filename=\"test.zip\"");
//creating byteArray stream, make it bufforable and passing this buffor to ZipOutputStream
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
ZipOutputStream zipOutputStream = new ZipOutputStream(bufferedOutputStream);
//simple file list, just for tests
ArrayList<File> files = new ArrayList<>(2);
files.add(new File("README.md"));
//packing files
for (File file : files) {
//new zip entry and copying inputstream with file to zipOutputStream, after all closing streams
zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
FileInputStream fileInputStream = new FileInputStream(file);
IOUtils.copy(fileInputStream, zipOutputStream);
fileInputStream.close();
zipOutputStream.closeEntry();
}
if (zipOutputStream != null) {
zipOutputStream.finish();
zipOutputStream.flush();
IOUtils.closeQuietly(zipOutputStream);
}
IOUtils.closeQuietly(bufferedOutputStream);
IOUtils.closeQuietly(byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
Но проблема в том, что с использованием кода, когда я ввожу URL: localhost: 8080/zip Я получаю файл: test.zip.html вместо zip файла
Когда я удаляю расширение .html и оставляю только test.zip, он правильно открывается, чтобы избежать возврата этого расширения .html? почему он добавлен?
Я понятия не имею, что еще я могу сделать. Я также пытался заменить ByteArrayOuputStream примерно следующим образом:
OutputStream outputStream = response.getOutputStream();
и установите метод void, чтобы он ничего не возвращал, но создал .zip файл, который был поврежден?
На моем macbook после распаковки test.zip я получал test.zip.cpgz, который снова давал мне файл test.zip и т.д.
В окнах файл .zip был поврежден, как я сказал, и даже не смог его открыть.
Я также предполагаю, что удаление расширения .html автоматически будет лучшим вариантом, но как?
Надеюсь, что это не так сложно, как кажется:) спасибо
Ответы
Ответ 1
похоже, решается. Я заменил:
response.setContentType("application/zip");
с:
@RequestMapping(value = "/zip", produces="application/zip")
И теперь я получаю ясный, красивый .zip файл:)
Если у кого-то из вас есть лучшее или более быстрое предложение или просто хотите дать какой-то совет, тогда вперед, мне любопытно.
Ответ 2
@RequestMapping(value="/zip", produces="application/zip")
public void zipFiles(HttpServletResponse response) throws IOException {
//setting headers
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader("Content-Disposition", "attachment; filename=\"test.zip\"");
ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());
// create a list to add files to be zipped
ArrayList<File> files = new ArrayList<>(2);
files.add(new File("README.md"));
// package files
for (File file : files) {
//new zip entry and copying inputstream with file to zipOutputStream, after all closing streams
zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
FileInputStream fileInputStream = new FileInputStream(file);
IOUtils.copy(fileInputStream, zipOutputStream);
fileInputStream.close();
zipOutputStream.closeEntry();
}
zipOutputStream.close();
}
Ответ 3
Я использую REST Web Service
Spring Boot
и я разработал конечные точки, чтобы всегда возвращать ResponseEntity
будь то JSON
или PDF
или ZIP
и я придумал следующее решение, которое частично вдохновлено denov answer
в этом вопросе, а также другим вопросом где я узнал, как преобразовать ZipOutputStream
в byte[]
чтобы ZipOutputStream
его ResponseEntity
качестве вывода конечной точки.
Во всяком случае, я создал простой класс утилиты с двумя методами для загрузки файлов в pdf
и zip
@Component
public class FileUtil {
public BinaryOutputWrapper prepDownloadAsPDF(String filename) throws IOException {
Path fileLocation = Paths.get(filename);
byte[] data = Files.readAllBytes(fileLocation);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
String outputFilename = "output.pdf";
headers.setContentDispositionFormData(outputFilename, outputFilename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
return new BinaryOutputWrapper(data, headers);
}
public BinaryOutputWrapper prepDownloadAsZIP(List<String> filenames) throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/zip"));
String outputFilename = "output.zip";
headers.setContentDispositionFormData(outputFilename, outputFilename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteOutputStream);
for(String filename: filenames) {
File file = new File(filename);
zipOutputStream.putNextEntry(new ZipEntry(filename));
FileInputStream fileInputStream = new FileInputStream(file);
IOUtils.copy(fileInputStream, zipOutputStream);
fileInputStream.close();
zipOutputStream.closeEntry();
}
zipOutputStream.close();
return new BinaryOutputWrapper(byteOutputStream.toByteArray(), headers);
}
}
И теперь конечная точка может легко вернуть ResponseEntity<?>
Как показано ниже, используя данные byte[]
и пользовательские заголовки, специально предназначенные для pdf
или zip
.
@GetMapping("/somepath/pdf")
public ResponseEntity<?> generatePDF() {
BinaryOutputWrapper output = new BinaryOutputWrapper();
try {
String inputFile = "sample.pdf";
output = fileUtil.prepDownloadAsPDF(inputFile);
//or invoke prepDownloadAsZIP(...) with a list of filenames
} catch (IOException e) {
e.printStackTrace();
//Do something when exception is thrown
}
return new ResponseEntity<>(output.getData(), output.getHeaders(), HttpStatus.OK);
}
BinaryOutputWrapper
- это простой неизменный POJO
класс, который я создал с помощью private byte[] data;
и org.springframework.http.HttpHeaders headers;
как поля, чтобы возвращать как data
и headers
из метода полезности.