首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从ColdFusion 11 CFZIP创建的Zip文件上的ColdFusion 9 CFZIP解压缩错误

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

Stack Overflow用户
提问于 2015-10-26 14:14:24
回答 1查看 1.2K关注 0票数 3

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

下面是生成用于导出的zip文件的代码:

代码语言:javascript
复制
<cfzip file="exportFileName.zip"
       source="#exportDirectory#" 
       action="zip" 
       overwrite="yes" 
       recurse="yes">

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

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

代码语言:javascript
复制
<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:只有泄气项才能有EXT描述符

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

每当我们测试它从ColdFusion 11源到ColdFusion 11目的地或从ColdFusion 9源到ColdFusion 9目的地时,这个过程就会正常工作。当我们在ColdFusion 11上拉链,试图在ColdFusion 9上解压缩时,我们才会遇到这个问题。我已经搜索过谷歌,但似乎找不到像这样的问题。任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

发布于 2015-11-20 10:26:14

TL/DR

  • CFZip on ColdFusion 11符合比PKZIP2.04g更晚的压缩规范
  • Java的ZipInputStream (似乎是CF9使用的)严格遵守了 2.04g (或更早版本)规范的

这两个版本之间一个微妙的区别是它们对零长度条目的处理(即子目录或mime类型等)。CFZip in CF11将这些条目标记为没有压缩的STORED (根据直觉和规范的后期版本,这很好,因为它们是零长度,所以压缩不会对它们造成任何影响),但是ZipInputStream (由CFZip在CF9中调用)希望使用DEFLATE方法将它们标记为压缩。

如果您没有零长度的条目,那么生成的文件是ColdFusion 可能会被读取(我认为它们会,但我不能确定地证明它)使用java的ZipInputStream;但是,如果您有零长度的文件,那么它就会抛出错误。

另一种办法是:

  • 在CF9中使用除CF9之外的其他内容,它能够读取符合以后标准的压缩文件(例如,您可以使用cfexecute在ColdFusion外部运行7zip或在ColdFusion中使用java org.apache.commons.compress库);或
  • CFZip中使用CF11之外的其他内容,这将将它创建的压缩文件限制为以前的标准(参见下面)。

详细答案:

来自压缩格式规范第4.4.4节的通用位标志:

位3:如果设置了此位,则本地标头中的字段crc-32、压缩大小和未压缩大小设置为零。在压缩数据之后,将正确的值放在数据描述符中。(注意:用于DOS的PKZIP版本2.04g只在方法8压缩时识别此位,更新版本的PKZIP对于任何压缩方法都识别此位。)

当本地文件头指示zip文件中有一个零长度条目(即一个目录或压缩文件中的某些其他东西,比如嵌入式mime类型)时,就会设置此位。

在PKZIP2.04g(或更早版本)标准下,它期望将压缩方法标志设置为DEFLATE (方法8压缩)。Java的ZipInputStream严格遵守的标准,如果找不到该压缩方法,则抛出一个ZipException (带有消息only DEFLATED entries can have EXT descriptor) (参见这里的源代码)。

当设置通用标志位时,cfzip似乎将压缩方法设置为STORED (方法0压缩-或不压缩)。据我所读,这符合PKZIP标准的后期版本,但与ZipInputStream类中Java实现的标准版本不向后兼容。

如何缓解这一问题:

  1. 要么不要在zip中存储零长度文件(这意味着我认为您不能在CFZip中使用CFZip选项);或者
  2. 不要在CFZip中使用CF11,如果您希望使用严格的向后可压缩性,并且使用其他的东西(请参见下面的建议)。
  3. 在CF11中使用CF11,但使用其他方法解压缩与更广泛的压缩算法兼容的CF9中的文件(即使用cfexecute调用外部程序来处理压缩文件;使用java org.apache.commons.compress库;等等)。

如果您选择了选项2,并且希望使用与耳麦版本兼容的东西,那么这对我有效,我可以使用ZipInputStream解压缩文件(在CF9中直接测试,因为我没有CF9):

代码语言:javascript
复制
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] );
                }
            }
        }
    }
}

基于这个答案

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

如果编译该java类并将.class文件放在类路径上的zip子目录中(您可以将条目添加到ColdFusion管理面板中)。

然后您可以使用以下方法调用它:

代码语言:javascript
复制
<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>

测试

如果您想要测试这种情况是如何发生的,那么这将在后面的规范下生成一个有效的zip文件,该文件将生成错误:

代码语言:javascript
复制
import java.io.*;
import java.util.zip.*;

public class TestZip {
    public static void main( final String[] args ) throws IOException {
        final File file = new File( args[0] );
        if ( file.exists() )
        {
            System.out.println( "File already exists" );
            return;
        }
        final boolean general_purpose_bit_flag_bit3_on = true;
        final byte gpbf = general_purpose_bit_flag_bit3_on ? 0x08 : 0x00;

        final byte[] contents = new byte[]{
            // Local File header
            'P', 'K', 3, 4,     // Local File Header Signature
            13, 0,              // Version needed to extract
            gpbf, 8,            // General purpose bit flag
            ZipEntry.STORED, 0, // Compression method
            'q', 'l', 't', 'G', // Last Modification time & date
            0, 0, 0, 0,         // CRC32
            0, 0, 0, 0,         // Compressed Size
            0, 0, 0, 0,         // Uncompressed Size
            12, 0,              // File name length
            0, 0,               // Extra field length
            'F', 'o', 'l', 'd', 'e', 'r', '_', 'n', 'a', 'm', 'e', '/',
                                // File name
            // Central directory file header
            'P', 'K', 1, 2,     // Central Directory File Header Signature
            13, 0,              // Version made by
            13, 0,              // Version needed to extract
            gpbf, 8,            // General purpose bit flag
            ZipEntry.STORED, 0, // Compression method
            'q', 'l', 't', 'G', // Last Modification time & date
            0, 0, 0, 0,         // CRC32
            0, 0, 0, 0,         // Compressed Size
            0, 0, 0, 0,         // Uncompressed Size
            12, 0,              // File name length
            0, 0,               // Extra field length
            0, 0,               // File comment length
            0, 0,               // Disk number where file starts
            0, 0,               // Internal File attributes
            0, 0, 0, 0,         // External File attributes
            0, 0, 0, 0,         // Relative offset of local header file
            'F', 'o', 'l', 'd', 'e', 'r', '_', 'n', 'a', 'm', 'e', '/',
                                // File name
            // End of Central Directory Record
            'P', 'K', 5, 6,     // Local File Header Signature
            0, 0,               // Number of this disk
            0, 0,               // Disk where CD starts
            1, 0,               // Number of CD records on this disk
            1, 0,               // Total number of records
            58, 0, 0, 0,        // Size of CD
            42, 0, 0, 0,        // Offset of start of CD
            0, 0,               // Comment length
        };
        try ( FileOutputStream fos = new FileOutputStream( file ) )
        {
            fos.write(contents);
        }
        try ( ZipInputStream zis = new ZipInputStream( new FileInputStream( file ) ) )
        {
            ZipEntry entry = zis.getNextEntry();
            System.out.println( entry.getName() );
        }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33347948

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档