【问题标题】:Create Zip File on Disk from Memory File从内存文件在磁盘上创建 Zip 文件
【发布时间】:2013-12-12 10:20:40
【问题描述】:

首先,请原谅我编码不佳!

要求:
1. 从数据库结果集在内存中创建 xls/xlsx 报告(即纯文本文件不应写入磁盘)。
2.从内存中的xlsx文件在磁盘上创建ZIP。

环境:
WinXP SP2、JDK1.6_06、Zip4j1.3.1、poi3.8

我正在使用 Apache 的 POI 和 Zip4j,并且正在关注 Mr.Shrikant 在“http://www.lingala.net/zip4j/forum/index.php?topic=257.0”上发布的示例

观察:
1. 该程序将 27,842 字节的 xlsx 文件写入磁盘以获取样本数据。
2.同一个Workbook创建ByteArrayOutputStream,baoStream的大小为49022bytes
3. 加密和压缩后创建大小为 43,084 字节的文件。
4. 解压 Zip 文件时,
a) WinZip,抛出错误“文件意外结束”
b) Winrar,抛出“CRC 错误”

请指正,哪里不对,哪里不好,请改进!

提前致谢!

package zipconversion;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import java.util.Random;
import net.lingala.zip4j.io.ZipOutputStream;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ZipCreationInMemory {

    ZipOutputStream zos = null;
    XSSFWorkbook workbook = null;
    ByteArrayOutputStream baoStream = null;
    String path = null;
    String xlsxfileExtn = null;
    String zipfileExtn = null;
    String onlyFileName = null;
    String xlsxFileName = null;
    String zipFileName = null;
    String xlsxFilePath = null;
    String zipFilePath = null;

    public static int randInt(int min, int max) {
        Random rand = new Random();
        int randomNum = rand.nextInt((max - min) + 1) + min;
        return randomNum;
    }

    public void createXlsxFile() {
        try {
            SimpleDateFormat timeFormat = new SimpleDateFormat("hh_mm_ss");
            path = "D:\\abcd\\";
            xlsxfileExtn = ".xlsx";
            zipfileExtn = ".zip";
            onlyFileName = "ReportData_".concat(timeFormat.format(new Date()));
            xlsxFileName = onlyFileName + xlsxfileExtn;
            zipFileName = onlyFileName + zipfileExtn;
            xlsxFilePath = path + xlsxFileName;
            zipFilePath = path + zipFileName;
            FileOutputStream out = new FileOutputStream(new File(xlsxFilePath));
            workbook = new XSSFWorkbook();
            XSSFSheet sheet = workbook.createSheet("Report");
            XSSFRow rowHead = sheet.createRow((short) 0);

            XSSFCellStyle headStyle = workbook.createCellStyle();
            XSSFFont headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerFont.setColor(new XSSFColor(new java.awt.Color(255, 0, 0)));
            headStyle.setFont(headerFont);
            headStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(255, 255, 255)));
            headStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);

            XSSFCellStyle oddStyle = workbook.createCellStyle();
            oddStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(randInt(125, 255), randInt(125, 255), randInt(125, 255))));
            oddStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);

//JDBC CONFIGURATIONS
            Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance();
            String dbURL = "jdbc:derby://localhost:1527/DATABASE_NAME;create=true;user=USER_ID;password=PASSWORD";

            Connection connection = DriverManager.getConnection(dbURL);
            Statement st = connection.createStatement();
            ResultSet resultSet = st.executeQuery("Select * from TABLE_NAME");
            ResultSetMetaData metaData = resultSet.getMetaData();
            int colCount = metaData.getColumnCount();

            SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy hh:mm:ss");



            for (int curColIndx = 0; curColIndx < colCount; curColIndx++) {
                XSSFCell cell = rowHead.createCell((short) curColIndx);
                cell.setCellStyle(headStyle);
                cell.setCellValue(metaData.getColumnName(curColIndx + 1));
            }
            int index = 1;
            while (resultSet.next()) {
                XSSFRow row = sheet.createRow((short) index);

                for (int curColIndx = 0; curColIndx < colCount; curColIndx++) {
                    XSSFCell cell = row.createCell((short) curColIndx);
                    if (index % 2 == 1) {
                        cell.setCellStyle(oddStyle);
                    }
                     else {
                        cell.setCellStyle(evenStyle);
                    }
                    int type = metaData.getColumnType(curColIndx + 1);
                    if (type == Types.TIMESTAMP) {
                        cell.setCellValue(sdf.format(resultSet.getDate(curColIndx + 1)));
                    } else if (type == Types.VARCHAR || type == Types.CHAR) {
                        cell.setCellValue(resultSet.getString(curColIndx + 1));
                    } else {
                        cell.setCellValue(resultSet.getLong(curColIndx + 1));
                    }
                }

                index++;
            }
            baoStream = new ByteArrayOutputStream();
            try {
//This Writes 27,842 bytes xlsx file to disk for sample data.
                workbook.write(out);
//same workbook is written to ByteArrayOutputStream()
                workbook.write(baoStream);
//But, baoStream size is 49022bytes and After Encryption and Zipping It Creates File of Size 43,084 bytes.
                System.out.println("baoStream.size() :" + baoStream.size());
                try {
                    //byte[] bytesToWrite = getBytesFromFile();
                    byte[] bytesToWrite = baoStream.toByteArray();
                    InMemoryOutputStream inMemoryOutputStream = new InMemoryOutputStream();

                    zos = new ZipOutputStream(inMemoryOutputStream);

                    ZipParameters parameters = new ZipParameters();
                    parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
                    parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
                    parameters.setFileNameInZip(xlsxFileName);
                    parameters.setSourceExternalStream(true);

                    zos.putNextEntry(null, parameters);
                    zos.write(bytesToWrite);
                    zos.closeEntry();
                    zos.finish();
                    zos.close();

                    // Write contents in our InMemoryOutputStream to a zip file to test if this worked
                    writeContentsToZipFile(inMemoryOutputStream);

                } catch (Exception e) {
                    e.printStackTrace();
                }

                out.close();
                resultSet.close();
                connection.close();
                System.out.println("Excel written successfully..");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (Exception e) {
            System.out.println("Exception is :" + e.toString());
        }
    }

    public ZipCreationInMemory() {
        //testZipCreationInMemory();
        createXlsxFile();
    }


package zipconversion;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Writes the content to memory.
 *
 */
public class InMemoryOutputStream extends OutputStream {

    // As we cannot know the size of the zip file that is being created,
    // we cannot maintain a byte array. We will copy all the bytes that
    // gets passed in the write() method to a List. Once all writing is done,
    // we can create a byte array from this List and this will be the content
    // of the zip file
    private List byteList;

    // flag to keep track if the outputstream is closed
    // no further write operations should be performed once this stream is closed
    private boolean closed;

    public InMemoryOutputStream() {
        byteList = new ArrayList();
        closed = false;
    }

    public void write(int b) throws IOException {
        if (closed) {
            throw new IOException("trying to write on a closed output stream");
        }

        byteList.add(Integer.toString(b));
    }

    public void write(byte[] b) throws IOException {
        if (b == null) {
            return;
        }
        write(b, 0, b.length);
    }

    public void write(byte[] b, int off, int len) throws IOException {
        if (closed) {
            throw new IOException("trying to write on a closed output stream");
        }

        if (b != null && len > 0) {
            for (int i = 0; i < len; i++) {
                byteList.add(Byte.toString(b[i]));
            }
        }
    }

    public byte[] getZipContent() {
        if (byteList.size() <= 0) {
            return null;
        }

        byte[] zipContent = new byte[byteList.size()+1];

        for (int i = 0; i < byteList.size(); i++) {
            zipContent[i] = Byte.parseByte((String) byteList.get(i));
        }
            return zipContent;
    }

    public void close() throws IOException {
        closed = true;
    }
}

【问题讨论】:

  • 你为什么打这个电话? zos.putNextEntry(null, parameters); 您在 zip 中添加了一个空条目,这些工具正确地提醒您注意您的损坏条目(您将其设为空)。
  • 你在单一方法中有太多事情要做。尝试改进它。
  • zos.putNextEntry(null, parameters);在压缩文件中添加文件名。

标签: java apache-poi zip4j


【解决方案1】:

该错误可能在方法writeContentsToZipFile(inMemoryOutputStream) 中,但您没有发布它的源代码...

我认为class InMemoryOutputStream的实现不是必需的,它没有效果并且会导致更多问题。

  • 如果要将 zip 内容保存到文件,请将其替换为 FileOutputStream
  • 如果要将zip内容保存在内存中,请将其替换为ByteArrayInputStream inMemoryOutputStream = new ByteArrayInputStream(bytesToWrite.length);

注意:zos.putNextEntry(null, parameters) 的第一个参数为空。 它在parameters.setSourceExternalStream(true) 时有效。在这种模式下,文件名和其他参数是通过 ZipParameters 提供的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-23
    • 2015-02-13
    • 1970-01-01
    • 2018-01-13
    • 1970-01-01
    • 2012-04-18
    • 2015-05-10
    • 2021-05-03
    相关资源
    最近更新 更多