4

JaxRSを使用してサーバーからzipファイルを作成して返したい。サーバー上に実際のファイルを作成したいとは思いません。可能であれば、zipをその場で作成し、それをクライアントに返したいと思います。その場で巨大なzipファイルを作成した場合、zipファイルに含まれるファイルが多すぎると、メモリが不足しますか?

また、これを行うための最も効率的な方法がわかりません。これが私が考えていたものですが、Javaでの入出力に関しては非常に錆びています。

public Response getFiles() {

// These are the files to include in the ZIP file       
String[] filenames = // ... bunch of filenames

byte[] buf = new byte[1024];

try {
   // Create the ZIP file
   ByteArrayOutputStream baos= new ByteArrayOutputStream();
   ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(baos));

   // Compress the files
   for (String filename : filenames) {
       FileInputStream in = new FileInputStream(filename);

       // Add ZIP entry to output stream.
       out.putNextEntry(new ZipEntry(filename));

       // Transfer bytes from the file to the ZIP file
       int len;
       while ((len = in.read(buf)) > 0) {
         out.write(buf, 0, len);
       }
       // Complete the entry
       out.closeEntry();
       in.close();
   }

   // Complete the ZIP file
   out.close();

   ResponseBuilder response = Response.ok(out);  // Not a 100% sure this will work
   response.type(MediaType.APPLICATION_OCTET_STREAM);
   response.header("Content-Disposition", "attachment; filename=\"files.zip\"");
   return response.build();

} catch (IOException e) {       
}

}

どんな助けでも大歓迎です。

4

2 に答える 2

2

次の 2 つのオプションがあります。

1- 一時ディレクトリに ZIP を作成し、クライアントにダンプします。

2- Response から OutputStream を使用して、zip を作成しているときにクライアントに直接送信します。

ただし、メモリを使用して巨大な ZIP ファイルを作成しないでください。

于 2012-08-18T07:28:53.410 に答える
0

クライアントに提供する前に、メモリ内の最初のバイトから最後のバイトまで ZIP ファイルを作成する必要はありません。また、事前に一時ディレクトリにそのようなファイルを作成する必要もありません (特に IO が非常に遅い可能性があるため)。

重要なのは、「ZIP 応答」のストリーミングを開始し、フライトでコンテンツを生成することです。

StreamaMethodReturningStream()を返す があり、各要素を ZIP ファイルに格納されたファイルに変換したいとします。また、コレクションや配列などの中間表現に各要素のバイトを常に格納しておきたくありません。

次に、そのような擬似コードが役立つ場合があります。

@GET
@Produces("application/zip")
public Response generateZipOnTheFly() {
    StreamingOutput output = strOut -> {
        try (ZipOutputStream zout = new ZipOutputStream(strOut)) {
            aMethodReturningStream().forEach(singleStreamElement -> {
                try {
                    ZipEntry zipEntry = new ZipEntry(createFileName(singleStreamElement));
                    FileTime fileTime = FileTime.from(singleStreamElement.getCreationTime());
                    zipEntry.setCreationTime(fileTime);
                    zipEntry.setLastModifiedTime(fileTime);
                    zout.putNextEntry(zipEntry);
                    zout.write(singleStreamElement.getBytes());
                    zout.flush();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    };
    return Response.ok(output)
                   .header("Content-Disposition", "attachment; filename=\"generated.zip\"")
                   .build();
}

StreamingOutputこの概念は、 aをResponseビルダーに渡すことに依存しています。はStreamingOutput、応答を送信する前に生成された完全な応答/エンティティ/本文ではなく、オンザフライでバイト フローを生成するために使用されるレシピです(ここでは にラップされていますZipOutputStream)。これについてよくわからない場合は、次にブレークポイントを設定して、flush()たとえばwget. ここで覚えておくべき重要なことは、ここでのストリームは、事前に計算されたアイテムまたは事前にフェッチされたアイテムの「ラッパー」ではないということです。DB カーソルなどをラップするなど、動的でなければなりません。また、データをストリーミングしているものに置き換えることもできます。foreachそのため、配列を反復するループにすることはできませんElement[] elems(それぞれElementがすべてのバイトを「内部」に持っています)。

for(Element elem: elems)

ZIP をストリーミングする前にすべてのアイテムを一度にヒープに読み込むことを避けたい場合。

(これは疑似コードであり、より良い処理を追加し、他のものも同様に磨き上げたい場合があることに注意してください。)

于 2020-10-27T13:11:45.760 に答える