2008-09-15 18 views
16

Tôi đang cố gắng đọc một tệp đơn từ java.util.zip.ZipInputStream và sao chép nó vào một java.io.ByteArrayOutputStream (để tôi có thể tạo một java.io.ByteArrayInputStream và trao cho thư viện của bên thứ ba sẽ kết thúc luồng và tôi không muốn đóng cửa ZipInputStream của mình).Đọc từ một ZipInputStream thành một ByteArrayOutputStream

Tôi có thể bị thiếu một cái gì đó cơ bản ở đây, nhưng tôi không bao giờ bước vào vòng lặp while ở đây:

ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream(); 
int bytesRead; 
byte[] tempBuffer = new byte[8192*2]; 
try { 
    while ((bytesRead = zipStream.read(tempBuffer)) != -1) { 
     streamBuilder.write(tempBuffer, 0, bytesRead); 
    } 
} catch (IOException e) { 
    // ... 
} 

tôi đang thiếu gì đó sẽ cho phép tôi để sao chép các dòng?

Edit:

Tôi có nên nói trước đó rằng ZipInputStream này không đến từ một tập tin, vì vậy tôi không nghĩ rằng tôi có thể sử dụng một ZipFile. Nó đến từ một tập tin được tải lên thông qua một servlet.

Ngoài ra, tôi đã gọi getNextEntry() trên số ZipInputStream trước khi đến đoạn mã này. Nếu tôi không thử sao chép tệp vào một số InputStream khác (thông qua số OutputStream được đề cập ở trên) và chỉ cần chuyển ZipInputStream vào thư viện của bên thứ 3, thư viện đóng luồng và tôi không thể làm gì khác, như xử lý các tệp còn lại trong luồng.

+1

Có thể bạn không quan tâm, nhưng ** bạn có thể tránh sao chép tất cả dữ liệu ** và tránh thư viện bên thứ ba đóng luồng nếu bạn quấn luồng đầu vào ban đầu (zipStream) và ghi đè phương thức đóng . 1) Tạo lớp công khai DontCloseInputStream mở rộng FilterInputStream. 2) Tạo một hàm tạo (InputStream in) gọi super (in) 3) Ghi đè lên phương thức close và không làm gì cả 4) Tạo mới DontCloseInputStream (zipStream) 5) chuyển nó vào thư viện. Và * với lá * – helios

+0

Và để sao chép một InputStream vào một OutputStream có một lớp tiện ích được gọi là Streams trong thư viện commons-fileupload (Apache). Bạn làm Streams.copy (trong, ngoài, đóng?) Và nó được thực hiện. – helios

+0

Vì vậy, zipEntry.getSize() trả về những gì? –

Trả lời

7

Vòng lặp của bạn có vẻ hợp lệ - mã sau đây (chỉ trên chính nó) trả về?

zipStream.read(tempBuffer) 

nếu nó trở về -1, thì zipStream sẽ bị đóng trước khi bạn nhận được, và tất cả mọi phiên cược sẽ bị tắt. Đã đến lúc sử dụng trình gỡ lỗi của bạn và đảm bảo những gì được chuyển cho bạn thực sự hợp lệ.

Khi bạn gọi getNextEntry(), nó trả về một giá trị và dữ liệu trong mục nhập có ý nghĩa không (tức là getCompressedSize() trả lại giá trị hợp lệ)? NẾU bạn chỉ đọc một tập tin Zip mà không có các mục zip đọc phía trước được nhúng, thì ZipInputStream sẽ không hoạt động cho bạn.

Một số mẩu tin hữu ích về định dạng Zip:

Mỗi tệp được nhúng trong tệp zip có tiêu đề. Tiêu đề này có thể chứa thông tin hữu ích (như độ dài nén của luồng, nó được bù đắp trong tệp, CRC) - hoặc nó có thể chứa một số giá trị ma thuật về cơ bản nói 'Thông tin không có trong tiêu đề luồng, bạn phải kiểm tra Zip post-amble '.

Mỗi tệp zip sau đó có bảng được đính kèm vào cuối tệp chứa tất cả các mục nhập zip cùng với dữ liệu thực. Bảng ở cuối là bắt buộc, và các giá trị trong nó phải chính xác. Ngược lại, các giá trị được nhúng trong luồng không cần phải được cung cấp.

Nếu bạn sử dụng ZipFile, nó sẽ đọc bảng ở cuối mã zip. Nếu bạn sử dụng ZipInputStream, tôi nghi ngờ rằng getNextEntry() cố gắng sử dụng các mục được nhúng trong luồng. Nếu những giá trị đó không được chỉ định, thì ZipInputStream không biết luồng có thể dài bao lâu. Thuật toán thổi phồng tự chấm dứt (bạn thực sự không cần phải biết độ dài không nén của luồng đầu ra để phục hồi hoàn toàn đầu ra), nhưng có thể phiên bản Java của trình đọc này không xử lý tình huống này rất tốt.

tôi sẽ nói rằng đó là khá bất thường để có một servlet trả lại một ZipInputStream (đó là phổ biến hơn nhiều để nhận một inflatorInputStream nếu bạn đang đi để được nhận nội dung nén.

+0

ZipInputStream trong java KHÔNG XỬ LÝ NÀY NÀY. Cảm ơn bạn đã đăng bài này. –

-1

Kiểm tra xem luồng đầu vào có được đặt trong chế độ ăn xin hay không.

Nếu không, khi triển khai: Tôi không nghĩ rằng bạn cần ghi vào luồng kết quả khi đang đọc, trừ khi bạn xử lý luồng chính xác này trong một chuỗi khác.

Chỉ cần tạo mảng byte, đọc luồng đầu vào, sau đó tạo luồng đầu ra.

0

Vẫn chưa rõ cách bạn nhận được zipStream. Nó sẽ hoạt động khi bạn nhận được nó như thế này:

zipStream = zipFile.getInputStream(zipEntry) 
+0

Tôi chỉ cần thêm một làm rõ về điều này, nhưng nó không phải là comiing từ một tập tin. – pkaeding

4

Tôi muốn sử dụng IOUtils từ dự án io commons.

IOUtils.copy(zipStream, byteArrayOutputStream); 
+0

Điều này có vẻ như nó có thể hoạt động. Tôi sẽ thử nó khi tôi đi làm vào ngày mai. Cảm ơn. – pkaeding

6

Bạn có thể cố gắng đọc từ một FileInputStream như thế này:

ZipInputStream in = new ZipInputStream(new FileInputStream(...));

này sẽ không làm việc kể từ một kho lưu trữ zip có thể chứa nhiều file và bạn cần phải xác định các tập tin để đọc.

Bạn có thể sử dụng java.util.zip.ZipFile và thư viện như IOUtils from Apache Commons IO hoặc ByteStreams from Guava để hỗ trợ bạn sao chép luồng.

Ví dụ:

ByteArrayOutputStream out = new ByteArrayOutputStream(); 
try (ZipFile zipFile = new ZipFile("foo.zip")) { 
    ZipEntry zipEntry = zipFile.getEntry("fileInTheZip.txt"); 

    try (InputStream in = zipFile.getInputStream(zipEntry)) { 
     IOUtils.copy(in, out); 
    } 
} 
+0

Tại sao tính năng này không hoạt động? Cảm ơn – Edmondo1984

+0

Tôi đã cập nhật câu trả lời của mình và thêm giải thích. –

0

t là không rõ ràng làm thế nào bạn có các zipStream. Nó sẽ làm việc khi bạn nhận được nó như thế này:

zipStream = zipFile.getInputStream(zipEntry) 

Nếu bạn đang thu thập các ZipInputStream từ một ZipFile bạn có thể nhận được một dòng cho thư viện của bên 3d, để cho nó sử dụng nó, và bạn có được một input stream sử dụng mã trước.

Hãy nhớ rằng, luồng vào là con trỏ. Nếu bạn có toàn bộ dữ liệu (như ZipFile), bạn có thể yêu cầu con trỏ N qua nó.

Một trường hợp khác là nếu bạn chỉ có luồng đầu vào "GZip", chỉ luồng byte được nén. Trong trường hợp đó bạn ByteArrayOutputStream đệm làm cho tất cả ý nghĩa.

1

Tôi sẽ gọi getNextEntry() trên ZipInputStream cho đến khi nó ở mục bạn muốn (sử dụng ZipEntry.getName() v.v.). Gọi getNextEntry() sẽ chuyển "con trỏ" tới đầu mục mà nó trả về. Sau đó, sử dụng ZipEntry.getSize() để xác định có bao nhiêu byte bạn nên đọc bằng cách sử dụng zipInputStream.read().

+0

Tôi thực sự đã gọi getNextEntry() trước khi đến đoạn mã này. Tôi vừa thêm một số giải thích rõ ràng cho câu hỏi. – pkaeding

3

Bạn có thể triển khai trình bao bọc của riêng mình quanh ZipInputStream bỏ qua close() và đưa nó vào thư viện của bên thứ ba.

thirdPartyLib.handleZipData(new CloseIgnoringInputStream(zipStream)); 


class CloseIgnoringInputStream extends InputStream 
{ 
    private ZipInputStream stream; 

    public CloseIgnoringInputStream(ZipInputStream inStream) 
    { 
     stream = inStream; 
    } 

    public int read() throws IOException { 
     return stream.read(); 
    } 

    public void close() 
    { 
     //ignore 
    } 

    public void reallyClose() throws IOException 
    { 
     stream.close(); 
    } 
} 
+0

Đây là một ý tưởng thú vị .... nếu không có gì khác hoạt động, tôi có lẽ sẽ cố gắng này. – pkaeding

0

Hãy thử mã dưới đây

private static byte[] getZipArchiveContent(File zipName) throws WorkflowServiceBusinessException { 

    BufferedInputStream buffer = null; 
    FileInputStream fileStream = null; 
    ByteArrayOutputStream byteOut = null; 
    byte data[] = new byte[BUFFER]; 

    try { 
    try { 
    fileStream = new FileInputStream(zipName); 
    buffer = new BufferedInputStream(fileStream); 
    byteOut = new ByteArrayOutputStream(); 

    int count; 
    while((count = buffer.read(data, 0, BUFFER)) != -1) { 
    byteOut.write(data, 0, count); 
    } 
    } catch(Exception e) { 
    throw new WorkflowServiceBusinessException(e.getMessage(), e); 
    } finally { 
    if(null != fileStream) { 
    fileStream.close(); 
    } 
    if(null != buffer) { 
    buffer.close(); 
    } 
    if(null != byteOut) { 
    byteOut.close(); 
    } 
    } 
    } catch(Exception e) { 
    throw new WorkflowServiceBusinessException(e.getMessage(), e); 
    } 
    return byteOut.toByteArray(); 

} 
2

Bạn đang thiếu gọi

ZipEntry entry = (ZipEntry) zipStream.getNextEntry();

để positio n byte đầu tiên được giải nén của mục nhập đầu tiên.

ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream(); 
int bytesRead; 
byte[] tempBuffer = new byte[8192*2]; 
ZipEntry entry = (ZipEntry) zipStream.getNextEntry(); 
try { 
    while ((bytesRead = zipStream.read(tempBuffer)) != -1){ 
     streamBuilder.write(tempBuffer, 0, bytesRead); 
    } 
} catch (IOException e) { 
     ... 
}