web-dev-qa-db-ja.com

Spring REST-.Zipファイルを作成してクライアントに送信する

バックエンドから受信した圧縮ファイルを含む.Zipファイルを作成し、このファイルをユーザーに送信します。 2日間、私は答えを探していて、適切な解決策を見つけることができませんでした、多分あなたは私を助けることができます:)

今のところ、コードは次のようになっています:(私はそれをスプリングコントローラーですべて行うべきではないことを知っていますが、それを気にしないでください、それはテスト目的で、それを動作させる方法を見つけるためです)

    @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を入力すると、.Zipファイルではなくtest.Zip.htmlが取得されることです。

。html拡張子を削除し、test.Zipだけを残すと、正しく開きますこの.html拡張子を返さないようにする方法なぜ追加されたのですか?

他に何ができるかわかりません。また、ByteArrayOuputStreamを次のようなものに置き換えようとしました。

OutputStream outputStream = response.getOutputStream();

そして、メソッドをvoidに設定して、何も返さないが、破損した.Zipファイルを作成しましたか?

test.Zipを解凍した後、私のMacbookでtest.Zip.cpgzを取得していましたが、これは再びtest.Zipファイルなどを提供してくれました。

私が言ったように、Windowsでは.Zipファイルが破損しており、開くことさえできませんでした。

また、.html拡張子を自動的に削除するのが最善の選択肢になると思いますが、どうすればいいですか?それがそうであるように難しいことではないことを願っています:)ありがとう

30
azalut

解決されたようです。私は置き換えました:

response.setContentType("application/Zip");

で:

@RequestMapping(value = "/Zip", produces="application/Zip")

そして今、私は明確な、美しい.Zipファイルを取得します:)

あなたのいずれかがより良いまたはより速い提案を持っているか、単にアドバイスをしたい場合は、先に進みます、私は興味があります。

27
azalut
@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();
}
21
denov

REST Web ServiceSpring Bootを使用しており、ResponseEntityまたはJSONまたはPDFであるかどうかにかかわらず、常にZipを返すようにエンドポイントを設計しました。この質問のdenov's answerと別の 質問 に触発された次の解決策を思い付きました。ここでは、ZipOutputStreambyte[]に順番に変換する方法を学びました。エンドポイントの出力としてResponseEntityにフィードします。

とにかく、pdfおよびZipファイルのダウンロード用の2つのメソッドを持つ単純なユーティリティクラスを作成しました

@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<?>データとpdfまたはZip用に特別に調整されたカスタムヘッダーを使用して、以下に示すようにbyte[]を簡単に返すことができます。

@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は、ユーティリティからPOJOdataの両方を返すためにprivate byte[] data;org.springframework.http.HttpHeaders headers;をフィールドとして作成した単純な不変headersクラスです。方法。

2
Raf