从ColdFusion 11 CFZIP创建的Zip文件上的ColdFusion 9 CFZIP解压缩错误

时间:2015-10-26 14:14:25

标签: java coldfusion

我支持的ColdFusion应用程序安装在两个不同的位置。一个位置运行带有ColdFusion 9和MS SQL Server 2008的Windows Server 2008,另一个位置运行带有ColdFusion 11和MS SQL Server 2012的Windows Server 2012.应用程序使用CFZIP action = "zip"提供导出过程,然后使用zip文件使用CFZIP action = "unzip"在目标计算机上导入。

以下是我必须生成用于导出的zip文件的代码:

<cfzip file="exportFileName.zip"
       source="#exportDirectory#" 
       action="zip" 
       overwrite="yes" 
       recurse="yes">

正确生成.zip文件,我可以在Windows资源管理器和7Zip中打开它,没有任何问题。

以下是解压缩上面创建的zip文件的代码:

<cfzip action="unzip" 
       file="exportFileName.zip" 
       destination="#destination#\xml" 
       recurse ="yes" 
       storepath="yes">

这与ColdFusion 9和ColdFusion 11实例上的代码相同,但是当我们尝试解压缩ColdFusion 9实例上的ColdFusion 11实例生成的zip文件时,我收到以下错误:

  

确保该文件是有效的zip文件并且可以访问。原因:java.util.zip.ZipException:只有DEFLATED条目可以有EXT描述符

我们在ColdFusion 9服务器上使用CFZIP解压缩在ColdFusion 11服务器上使用CFZIP生成的zip文件时才会遇到此问题。我可以在ColdFusion 9服务器上提取从ColdFusion 11生成的zip文件的内容,使用7Zip创建一个新的Zip文件,其中包含原始ColdFusion 11生成的zip文件的确切内容,我没有得到错误。

每当我们测试从ColdFusion 11源到ColdFusion 11目的地或从ColdFusion 9源到ColdFusion 9目的地时,该过程都能正常工作。我们只是在使用ColdFusion 11并试图解压ColdFusion 9时遇到问题。我已经搜索过Google,但似乎无法找到这样的问题。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

<强> [TL / DR]

    ColdFusion 11上的
  • CFZip符合比PKZIP 2.04g晚的zip规范
  • Java ZipInputStream(CF9似乎使用)符合严格符合PKZIP 2.04g(或更早版本)规范。

两个版本之间的细微差别是它们处理零长度条目(即子目录或mime类型等)。 CF11中的CFZip将这些条目标记为STORED,没有压缩(直观地,在规范的更高版本中,它是正常的,因为它们是零长度,因此压缩不会对它们做任何事情)但是ZipInputStream(在CF9中由CFZip调用)期望使用DEFLATE方法将它们标记为压缩。

如果您没有零长度条目,那么生成的文件是ColdFusion 可能能够使用java {{1}读取(我认为他们会但最终无法证明) };但是,如果你有零长度的文件,那么它会抛出错误。

或者,要么:

  • 在CF9中使用ZipInputStream以外的其他内容,能够读取符合以后标准的zip文件(即您可以使用CFZip在ColdFusion外部运行7zip或使用java cfexecute ColdFusion里面的库);或
  • 在CF11中使用org.apache.commons.compress以外的内容,将其创建的zip文件限制为早期标准(见下文)。

详细解答:

Zip Format Specification部分4.4.4通用位标志:

  

位3:如果设置此位,则压缩字段crc-32          大小和未压缩的大小在中设置为零          本地标题。正确的值放在          紧接着压缩后的数据描述符          数据。 (注意:仅限DOS的PKZIP版本2.04g          识别此位用于方法8压缩,更新          PKZIP的版本可以识别这个位          压缩方法。)

当本地文件头指示zip文件中存在零长度条目时(即一个目录或zip文件中的某些其他内容,如嵌入式mime类型),此位置位。

在PKZIP版本2.04g(或更早版本)标准下,它会期望压缩方法标志设置为CFZip(方法8压缩)。 Java的DEFLATE符合严格到此标准,如果找不到此压缩方法,则会抛出ZipInputStream(带有消息ZipException)(请参阅source code here })。

当设置通用标志位时,

only DEFLATED entries can have EXT descriptor似乎将压缩方法设置为cfzip(方法0压缩 - 或不压缩)。根据我的阅读,这符合PKZIP标准的更高版本,但不向后兼容STORED类中Java实现的标准版本。

如何缓解此问题:

  1. 要么不在zip中存储零长度文件(这意味着我认为你不能使用ZipInputStream中的recurse选项);或
  2. 如果您想要严格的向后兼容性并使用其他内容,请不要在CF11中使用CFZip
  3. 在CF11中使用CFZip但使用其他内容解压缩CF9中与更多压缩算法兼容的文件(即使用CFZip调用外部程序来处理zip文件;使用java cfexecute库;等)。
  4. 如果你选择2并希望使用与eariler版本兼容的东西,那么这对我有用,我可以使用org.apache.commons.compress解压缩文件(直接在Java中测试,因为我没有CF9):

    ZipInputStream

    Based on this answer

    (注意:错误处理是最小的,因此如果您打算在生产环境中使用它,您可能希望使其更加健壮。)

    如果编译该java类并将package zip; import java.io.*; import java.util.zip.*; public class Zip { private static void processFolder( final File folder, final ZipOutputStream zos, final boolean recurse, final int prefixLength, final byte[] buffer ) throws IOException { for ( final File file : folder.listFiles() ) { if ( file.isFile() ) { final String name = file.getPath().substring( prefixLength ); // System.out.println( name ); final ZipEntry entry = new ZipEntry( name ); zos.putNextEntry(entry); try (FileInputStream is = new FileInputStream( file ) ){ int read; while( (read = is.read( buffer ) ) != -1 ) { zos.write( buffer, 0, read ); } } zos.closeEntry(); } else if ( recurse && file.isDirectory() ) { processFolder( file, zos, recurse, prefixLength, buffer ); } } } public static void zipFolder( final String folderPath, final String outputName, final boolean recurse, final boolean overwrite ) throws IOException { final File folder = new File( folderPath ); if ( folder.exists() && folder.isDirectory() ) { final File output = new File( outputName ); if ( overwrite || !output.exists() ) { try ( ZipOutputStream zos = new ZipOutputStream( new FileOutputStream( output ) ) ) { processFolder( folder, zos, recurse, folder.getPath().length() + 1, new byte[1024*4] ); } } } } } 文件放在类路径的.class子目录中(可以在ColdFusion管理面板中添加条目)。

    然后你可以使用:

    来调用它
    zip

    <强>测试

    如果你想测试一下这是怎么回事,那么这会产生一个有效的zip文件,在后面的特定情况下会生成错误:

    <cfscript>
      zip = CreateObject( "java", zip.Zip" );
      zip.zipFolder(
        "/path/to/folder/to/be/zipped/",
        "/path/to/output/zip/file.zip",
        true, // recurse
        true  // overwrite existing file
      );
    </cfscript>