使用Apache POI创建一个密码保护的Excel文件?

13

我正在使用 (Apache POI) API 开发一个简单的 Java 程序来创建 Excel 文件。 我正在使用 Oracle 10g 作为数据库,并使用 ojdbc14 JAR 文件。

我有一个名为 USERINFO 的表,其中有三列,分别是 USERNAMEPASSWORDNAME。 现在使用 Apache POI,我已经能够将所有行放入一个 Excel 文件中。

由于文件包含敏感数据,如用户名和密码,因此我想将其设置为受密码保护的。 在论坛上,我找到了如何读取受密码保护的文件,但不知道如何创建它们。 那么我该如何实现呢?

4个回答

16

更新:从3.10版本开始,POI支持对XLSX文件进行加密和解密。请参阅POI网站上的“加密支持”页面。以下内容仍适用于XLS二进制工作簿。

根据 POI网站上的“加密支持”页面,POI支持读取加密的XLS和XLSX文件。该页面未提及加密,这意味着不支持该功能。搜索POI网站上的“加密”也证实了这一点,返回的结果很少且全部与解密有关。我还查看了他们加密实现的源代码,发现只处理解密。这并不奇怪;POI旨在进行数据提取和搜索索引,而非创建新的电子表格。 正如其他人所建议的,通常可以通过在Excel中创建模板,然后使用POI填充数据来解决POI中缺少的功能。不幸的是,对于加密,这种方法行不通,因为加密电子表格的文件格式与非加密电子表格截然不同。
如果您愿意支付商业软件费用,ExtenXLS的最新版本支持Excel支持的所有加密格式的完整读写支持。只需构建一个EncryptedWorkBookHandle而不是普通的WorkBookHandle。这将使用未修改的JRE支持的最强密码,XLS使用RC4,XLSX使用128位AES。如果您想在OOXML中使用256位AES,并且已安装了JCE无限制策略,则可以使用MSOfficeEncrypter类实现。

JExcelAPI,一款流行的开源Java电子表格API,似乎完全不支持加密。Aspose.Cells是一款商业软件,支持强加密。Actuate的e.Spreadsheet文档似乎已经从网络上消失了,因此我无法确定它是否支持加密。

由于没有免费的Java电子表格API支持编写加密电子表格,如果您不想使用商业软件,则需要想出解决方法。例如,您可以将电子表格写入加密的ZIP文件中。 java.util.zip不支持加密,但看起来Zip4j支持。

完整披露:我在Extentech工作,该公司是ExtenXLS背后的公司。


3
最近我为POI增加了对基于XML的文件的加密支持,所以如果你更新你的回答,即第一段过时了,那就太好了。至于抽取方面的设计:大多数POI-API都是从读取功能开始支持的,但我不认为POI是专门为抽取而设计的(在我看来,这也适用于2012年1月)。 - kiwiwings
1
同意 kiwiwings 的观点。之前的回答有误,查看最新文档后发现已支持加密。请更新该回答。 - vinayknl
@kiwiwings,我终于更新了。顺便说一下,您不需要等待原作者更新内容。StackOverflow类似于维基百科,因此您可以随时提出编辑建议 - Sam Hanes

8
创建一个密码保护的Excel文件,或者使用现有的模板并将其设置为密码保护。这将使用户只能以“只读”方式访问。以下是一个示例,其中我有一个带有密码“secret”的Excel文件:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;

public class ProtectedExcelFile {

    public static void main(final String... args) throws Exception {

        String fname = "C:\\Documents and Settings\\sadutta\\Desktop\\sample.xls";

        FileInputStream fileInput = null;
        BufferedInputStream bufferInput = null;
        POIFSFileSystem poiFileSystem = null;
        FileOutputStream fileOut = null;

        try {
            fileInput = new FileInputStream(fname);
            bufferInput = new BufferedInputStream(fileInput);
            poiFileSystem = new POIFSFileSystem(bufferInput);

            Biff8EncryptionKey.setCurrentUserPassword("secret");
            HSSFWorkbook workbook = new HSSFWorkbook(poiFileSystem, true);
            HSSFSheet sheet = workbook.getSheetAt(0);

            HSSFRow row = sheet.createRow(0);
            Cell cell = row.createCell(0);

            cell.setCellValue("THIS WORKS!");

            fileOut = new FileOutputStream(fname);
            workbook.writeProtectWorkbook(Biff8EncryptionKey.getCurrentUserPassword(), "");
            workbook.write(fileOut);
        }
        catch (Exception ex) {

            System.out.println(ex.getMessage());
        }
        finally {
            try {

                bufferInput.close();
            }
            catch (IOException ex) {

                System.out.println(ex.getMessage());
            }

            try {

                fileOut.close();
            }
            catch (IOException ex) {

                System.out.println(ex.getMessage());
            }
        }
    }
}

同样的方式,您应该能够编写或修改现有的模板。完成后,请覆盖模板。如果您的模板需要多次使用,则可能希望将模板复制到其他位置,然后使用代码进行修改。


1
请注意,尽管此代码确实创建了一个受保护的Excel工作簿,但生成的文件并未加密。密码被散列并存储在未更改的文件中。由读取文件的软件来执行此类保护。使用适当的加密,无法在没有密码的情况下读取文件。 - Sam Hanes
我生成了一个带密码保护的Excel文件,但是它无法通过Excel工具打开。我可以使用Plan Maker工具打开。有人遇到过这种情况吗? - Raja

4
我经常发现在使用POI处理更复杂的任务时,一个有用的方法是在Excel中创建具有高级功能(例如宏)的电子表格,然后使用POI读取电子表格,填充数据并将其写出。POI通常会保留电子表格的功能并添加数据。我没有尝试过这个方法来处理密码,但我认为值得一试。有关更多信息,请参见忙碌开发者指南

嗨,布赖恩,我对宏或高级功能一无所知。你能给我一些链接,在那里我可以得到一些帮助吗?我是POI的新手。 - vikiiii
我所建议的只是在Excel中创建具备您需要功能的电子表格,然后使用POI来读取并填充它。也就是说,这与您正在做的事情非常相似,只不过使用的是现有的电子表格而不是新的。 - Brian Agnew
你告诉我了一种可选的方法。我在问题中提出的只是一个例子。实际上,在我的项目中有很多表格,如果我为每个表格手动创建Excel文件,那将会很困难。 - vikiiii
1
为什么被踩了?如果你知道上述方法不起作用,请告诉我,我会根据需要删除/修改。 - Brian Agnew

0
以下程序将在给定的Excel路径中生成受密码保护的Excel文件。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class MyTest1 {
    public static void main(String[] args) {
        File file = new File("C:\\Users\\Raju\\Desktop\\workbook1.xlsx");
        try {
            file.createNewFile();
            OutputStream fileOut = new FileOutputStream(file);
            XSSFWorkbook wb = new XSSFWorkbook();
            Sheet sheet = wb.createSheet();
            Row row = sheet.createRow(0);
            Cell cell = row.createCell(0);
            cell.setCellValue("Venu");
            wb.write(fileOut);
            wb.close();
            fileOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (POIFSFileSystem fs = new POIFSFileSystem()) {
            EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
            // EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile,
            // CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null);
            Encryptor enc = info.getEncryptor();
            enc.confirmPassword("hello");
            // Read in an existing OOXML file and write to encrypted output stream
            // don't forget to close the output stream otherwise the padding bytes aren't
            // added
            try (OPCPackage opc = OPCPackage.open(file, PackageAccess.READ_WRITE);
                    OutputStream os = enc.getDataStream(fs)) {
                opc.save(os);
            } catch (InvalidFormatException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (GeneralSecurityException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            // Write out the encrypted version
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fs.writeFilesystem(fos);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (IOException e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        }
        System.out.println("Excel file exported");
    }
}

使用了Maven依赖

<dependency>
 <groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>

是的,我知道我们可以优化很多代码。在高层次上,程序正在工作。


这段代码对我来说不起作用 :( - Ivan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接