libzip函数返回成功但无实际操作的问题排查与解决
libzip创建ZIP文件问题调试与解决
问题描述
我正在Windows 11系统下使用Visual Studio 2022和C语言学习libzip库的使用,目标是创建包含多文件夹多文件的ZIP文件。目前我仅需创建基础ZIP文件,但库的表现不符合预期:明明未执行任何可检测的请求操作,却返回成功状态。我并非仅寻求代码修复(当然也不拒绝),更想了解如何调试这种“返回成功但明显失败”的库问题。
测试现象
- 当定义
_UseFqn_并使用完整路径文件名(fqn)执行以下基础流程时,能成功创建ZIP文件(Windows无法读取但7-Zip可以,暂可接受):
zip_open() zip_source_file(fqn) zip_file_add(fqn) zip_close()
- 当调用
SetCurrentDirectory()切换到文件所在目录(fpath),尝试添加无路径信息的文件(fname)时,无错误返回但也未生成ZIP文件:
zip_open() SetCurrentDirectory(fpath) zip_source_file(fname) zip_file_add(fname) zip_close()
- 无论使用上述哪种流程,调用
zip_dir_add()添加目录时,同样无错误返回但未生成ZIP文件。
测试源码
假设调用方式为:_MakeZipFile("testfile.zip","C:\path\to\file\something.log\0");
// ------------------------------------------------------------------------- // Add a single file to the zip static int _AddFileToZip( zip_t *zipFile, char *szFileName ) { int rc = NO_ERROR; zip_source_t *source; source = zip_source_file( zipFile, szFileName, 0, ZIP_LENGTH_TO_END ); if( source == NULL ) { printf( "ZipTest> Failed to src file for zip: '%s'...\n", szFileName ); printf( "ZipTest> libzip: %s\n", zip_strerror( zipFile ) ); rc = ERROR_INVALID_PARAMETER; } else if( zip_file_add( zipFile, szFileName, source, ZIP_FL_ENC_RAW ) < 0 ) { zip_source_free( source ); printf( "ZipTest> Failed to add file to zip: '%s'...\n", szFileName ); printf( "ZipTest> libzip: %s\n", zip_strerror( zipFile ) ); rc = ERROR_INVALID_FUNCTION; } else { // print a diagnostic for debugging printf( "ZipTest> added file: '%s'...\n", szFileName ); } return rc; } // ------------------------------------------------------------------------- // Add all folder levels, from top to bottom, recursively static BOOL _AddFolderToZip( zip_t *zipFile, char *szFolder ) { char sz[_MAX_PATH]; char *cp; BOOL bResult; zip_error_t *pze; if( ':' == szFolder[1] ) { // Remove drive letter, if present szFolder += 2; } if( '\\' == *szFolder ) { // Remove leading backslash, if present szFolder++; } strcpy( sz, szFolder ); // copy so we don't trash the original cp = strrchr( sz, '\\' ); if( cp && ('\0' == *(cp+1)) ) { // Remove trailing backslash *cp = '\0'; } // Is this a sub-folder? cp = strrchr( sz, '\\' ); if( cp && (cp != sz) ) { // Yes, call self to add parent first *cp = '\0'; if( !_AddFolderToZip( zipFile, sz ) ) { // Failure, back out with errors return FALSE; } *cp = '\\'; } // else No, time to add this folder. bResult = TRUE; if( zip_dir_add( zipFile, sz, ZIP_FL_ENC_RAW ) < 0 ) { // Error, but there's one error that is OK... pze = zip_get_error( zipFile ); if( ZIP_ER_EXISTS != pze->zip_err ) { // Multiple files in same folder are OK. bResult = FALSE; printf( "ZipTest> Failed to add directory to zip: '%s'...\n", szFolder ); printf( "ZipTest> libzip: %s\n", zip_strerror( zipFile ) ); } } if( bResult ) { // print a diagnostic for debugging printf( "ZipTest> added directory: '%s'...\n", sz ); } return bResult; } // ------------------------------------------------------------------------ #define _UseFqn_ 1 // Use Fully qualified file names and no directories // comment this out to change directory, add the file folder, and add the filename // ------------------------------------------------------------------------ // szzSourceList is a buffoer containing some number of NULL terminated // strings that ends with an empty string (i.e. double-NULL) BOOL _MakeZipFile( char *szZipFileName, char *szzSourceList ) { BOOL result = FALSE; int errorp = NO_ERROR; zip_t *zipFile; char *cpFilePath; zip_error_t ziperror; zip_error_t *pze; #if !defined(_UseFqn_) char szDrive[_MAX_PATH]; char szPath[_MAX_PATH]; char szFile[_MAX_PATH]; char szExt[_MAX_PATH]; char szFolder[_MAX_PATH]; #endif char szStartPath[_MAX_PATH]; GetCurrentDirectory( sizeof(szStartPath), szStartPath ); zipFile = zip_open( szZipFileName, ZIP_CREATE | ZIP_TRUNCATE, &errorp ); if( zipFile == NULL ) { zip_error_init_with_code( &ziperror, errorp ); printf( "ZipTest> Failed to open output file '%s'...\n", szZipFileName ); printf( "ZipTest> libzip: %s\n", zip_error_strerror( &ziperror ) ); return FALSE; } __try { cpFilePath = szzSourceList; while( *cpFilePath ) { #if defined(_UseFqn_) _AddFileToZip( zipFile, cpFilePath ); #else _splitpath( cpFilePath, szDrive, szPath, szFile, szExt ); sprintf( szFolder, "%s%s", szDrive, szPath ); SetCurrentDirectory( szFolder ); _AddFolderToZip( zipFile, szPath ); strcat( szFile, szExt ); _AddFileToZip( zipFile, szFile ); #endif // Find next entry in \0\0 terminated string list while( *cpFilePath ) { cpFilePath++; } cpFilePath++; } } __finally { if( NO_ERROR == zip_close( zipFile ) ) { result = TRUE; } else { pze = zip_get_error( zipFile ); printf( "ZipTest> Failed to create output file '%s'...\n", szZipFileName ); printf( "ZipTest> libzip: %s\n", zip_strerror( zipFile ) ); printf( "ZipTest> zip_err = %d sys_err = %d\n", pze->zip_err, pze->sys_err ); } } SetCurrentDirectory( szStartPath ); return result; }
问题解决关键点(调试libzip源码后发现)
zip_open()函数并不会实际创建文件,仅保存文件名供后续zip_close()使用。如果在处理过程中更改了当前目录且未在调用zip_close()前恢复,ZIP文件会被创建在最后设置的当前目录下。这属于库的设计缺陷——若zip_open()调用未指定路径,应将当前目录预先添加到保存的ZIP文件名中,以便zip_close()正确使用。- 除了传递给
zip_source_file()的文件名需要完整路径外,移除所有文件夹和文件名中的驱动器号并保留反斜杠后,操作效果更好。而zip_file_add()在文件的“半限定名”开头不含C:\(或其他驱动器号)时表现更佳。
内容的提问来源于stack exchange,提问作者Steve Valliere
相关产品推荐
相关产品推荐

