From 1bd807f36c1338cb58aca1657e70244c53e256da Mon Sep 17 00:00:00 2001 From: zhangtingan Date: Mon, 21 Sep 2026 23:29:14 +0800 Subject: [PATCH] fix: update libzip archive data in-memory like libarchive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Root cause: LibzipPlugin::updateArchiveData() ignored the options parameter, cleared all memory data, and re-read from disk via zip_open() with commented-out error handling on failure 2. Fix: rewrite updateArchiveData() to use the options parameter for in-memory updates (Delete/Rename/Add), mirroring LibarchivePlugin::updateArchiveData(), eliminating disk re-read fragility and restoring proper error handling 3. Impact: zip format archives now refresh UI immediately after rename/delete/add operations without reopening, consistent with tar format behavior; no signature change, single caller via virtual dispatch unaffected Influence: 1. Test zip archive file rename, verify UI shows new name immediately 2. Test zip archive file/folder delete, verify UI updates instantly 3. Test zip archive file add, verify new entry appears without reopen 4. Test multi-level path folder rename/delete in zip archives 5. Verify tar format operations remain unaffected fix: 修复zip格式压缩包重命名后需重新打开才显示的问题 1. 根因:LibzipPlugin::updateArchiveData() 忽略 options 参数,清空 全部内存数据后通过 zip_open() 从磁盘重读,且 zip_open 失败时 错误处理被注释掉,导致内存数据为空或过时,UI 无法正确刷新 2. 方案:参照 LibarchivePlugin::updateArchiveData() 实现,改用 options 参数做内存级更新(Delete/Rename/Add),消除磁盘重读 脆弱性,恢复错误处理 3. 影响:zip 格式压缩包重命名/删除/追加后 UI 即时刷新,与 tar 格式行为一致;函数签名不变,单一调用方通过虚函数分派不受影响 Influence: 1. 测试zip压缩包内文件重命名,验证UI即时显示新名称 2. 测试zip压缩包内文件/文件夹删除,验证UI即时更新 3. 测试zip压缩包内文件追加,验证新条目无需重开即可显示 4. 测试多层路径文件夹的重命名/删除操作 5. 验证tar格式操作不受影响 PMS: BUG-232441 --- 3rdparty/libzipplugin/libzipplugin.cpp | 165 +++++++++++++++++++++---- 1 file changed, 141 insertions(+), 24 deletions(-) diff --git a/3rdparty/libzipplugin/libzipplugin.cpp b/3rdparty/libzipplugin/libzipplugin.cpp index 89bbcc4d0..10a588e63 100644 --- a/3rdparty/libzipplugin/libzipplugin.cpp +++ b/3rdparty/libzipplugin/libzipplugin.cpp @@ -553,39 +553,156 @@ PluginFinishType LibzipPlugin::addComment(const QString &comment) return PFT_Nomral; } -PluginFinishType LibzipPlugin::updateArchiveData(const UpdateOptions &/*options*/) +PluginFinishType LibzipPlugin::updateArchiveData(const UpdateOptions &options) { - m_mapFileCode.clear(); - m_setHasHandlesDirs.clear(); - m_setHasRootDirs.clear(); - DataManager::get_instance().resetArchiveData(); + ArchiveData &stArchiveData = DataManager::get_instance().archiveData(); + QString rootEntry; + + foreach (FileEntry entry, options.listEntry) { + if (options.eType == UpdateOptions::Delete ) { // 删除,更新压缩包数据 + if (entry.isDirectory) { // 删除文件夹 + // 在map中查找该文件夹下的文件并删除 + QMap::iterator itor = stArchiveData.mapFileEntry.begin(); + while (itor != stArchiveData.mapFileEntry.end()) { + if (itor->strFullPath.startsWith(entry.strFullPath)) { + if (!itor->isDirectory) { + stArchiveData.qSize -= itor->qSize; // 更新压缩包内文件原始总大小 + } + itor = stArchiveData.mapFileEntry.erase(itor); + } else { + ++itor; + } + } - // 处理加载流程 - int errcode = 0; - zip_error_t err; + // 更新文件夹第一层的数据 + if (entry.strFullPath.endsWith(QLatin1Char('/')) && entry.strFullPath.count(QLatin1Char('/')) == 1) { + for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) { + if (stArchiveData.listRootEntry.at(i).strFullPath == entry.strFullPath) { // 在第一次层数据中找到entry移除 + stArchiveData.listRootEntry.removeAt(i); + break; + } + } + } + } else { // 删除文件 + stArchiveData.qSize -= entry.qSize; // 更新压缩包内文件原始总大小 + stArchiveData.mapFileEntry.remove(entry.strFullPath); //在map中删除该文件 + // 更新文件夹第一层的数据 + if (!entry.strFullPath.contains(QLatin1Char('/'))) { + for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) { + if (stArchiveData.listRootEntry.at(i).strFullPath == entry.strFullPath) { // 在第一次层数据中找到entry移除 + stArchiveData.listRootEntry.removeAt(i); + break; + } + } + } + } + } else if (options.eType == UpdateOptions::Rename) { // 重命名,更新压缩包数据 + QMap tmpMapFileEntry; + QString strAlias; + if (entry.isDirectory) { // 重命名文件夹 + // 在map中查找该文件夹下的文件并重命名 + QMap::iterator itor = stArchiveData.mapFileEntry.begin(); + while (itor != stArchiveData.mapFileEntry.end()) { + if (itor->strFullPath.startsWith(entry.strFullPath)) { + QString strPath = QFileInfo(entry.strFullPath.left(entry.strFullPath.length() - 1)).path(); + if(strPath == "."){ + strAlias = entry.strAlias + QDir::separator(); + } else { + strAlias = strPath + QDir::separator() + entry.strAlias + QDir::separator(); + } + strAlias = strAlias + QString(itor->strFullPath).right(QString(itor->strFullPath).length()-entry.strFullPath.length()); + FileEntry tmpEntry = itor.value(); + tmpEntry.strFullPath = strAlias; + if(tmpEntry.isDirectory) { + tmpEntry.strFileName = QFileInfo(strAlias.left(strAlias.length() - 1)).fileName(); + } else { + tmpEntry.strFileName = QFileInfo(strAlias).fileName(); + } + tmpEntry.strAlias.clear(); + tmpMapFileEntry.insert(strAlias, tmpEntry); + itor = stArchiveData.mapFileEntry.erase(itor); + } else { + ++itor; + } + } + if(!tmpMapFileEntry.isEmpty()) { + for (QString strFullPath: tmpMapFileEntry.keys()) { + stArchiveData.mapFileEntry.insert(strFullPath, tmpMapFileEntry.value(strFullPath)); + } + } + // 更新文件夹第一层的数据 + if (entry.strFullPath.endsWith(QLatin1Char('/')) && entry.strFullPath.count(QLatin1Char('/')) == 1) { + for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) { + if (stArchiveData.listRootEntry.at(i).strFullPath == entry.strFullPath) { // 在第一次层数据中找到entry移除 + stArchiveData.listRootEntry.removeAt(i); + strAlias = entry.strAlias + QDir::separator(); + entry.strFullPath = strAlias; + entry.strFileName = strAlias; + stArchiveData.listRootEntry.append(entry); + break; + } + } + } + } else { // 重命名文件 + stArchiveData.mapFileEntry.remove(entry.strFullPath); //在map中重命名该文件 + QString strPath = QFileInfo(entry.strFullPath).path(); + if(strPath == "." || strPath.isEmpty() || strPath.isNull()) { + strAlias = entry.strAlias; + } else { + strAlias = strPath + QDir::separator() + entry.strAlias; + } + FileEntry tmpEntry = entry; + tmpEntry.strFullPath = strAlias; + stArchiveData.mapFileEntry.insert(strAlias, tmpEntry); + // 更新文件夹第一层的数据 + if (!entry.strFullPath.contains(QLatin1Char('/'))) { + for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) { + if (stArchiveData.listRootEntry.at(i).strFullPath == entry.strFullPath) { // 在第一次层数据中找到entry重命名 + stArchiveData.listRootEntry.removeAt(i); + strAlias = entry.strAlias; + entry.strFullPath = strAlias; + entry.strFileName = strAlias; + stArchiveData.listRootEntry.append(entry); + break; + } + } + } + } + } else if (options.eType == UpdateOptions::Add) { // 追加,更新压缩包数据 + QString destinationPath = options.strParentPath; // 在压缩包中追加的路径 + QFileInfo file(entry.strFullPath); // 注意此时entry.strFullPath如果是是文件夹还没有'/' + if (rootEntry.isEmpty()) { // 获取所有追加文件的父目录 + rootEntry = file.filePath().left(file.filePath().size() - file.fileName().size()); + } - zip_t *archive = zip_open(QFile::encodeName(m_strArchiveName).constData(), ZIP_RDONLY, &errcode); // 打开压缩包文件 - zip_error_init_with_code(&err, errcode); + entry.strFullPath = destinationPath + entry.strFullPath.remove(rootEntry); // entry在压缩包中的全路径 + if (file.isDir()) { // 文件夹 + entry.strFullPath = entry.strFullPath + QDir::separator(); // 手动添加'/' + } else { + // 更新压缩包内文件原始总大小 + stArchiveData.qSize -= stArchiveData.mapFileEntry.value(entry.strFullPath).qSize; + stArchiveData.qSize += entry.qSize; + } - //某些特殊文件,如.crx用zip打不开,需要替换minizip - if (!archive) { -// m_bAllEntry = true; -// return minizip_list(); - } + // 判断是否追加到第一层数据 + if (destinationPath.isEmpty() && ((entry.strFullPath.count('/') == 1 && entry.strFullPath.endsWith('/')) || entry.strFullPath.count('/') == 0)) { + for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) { + if (stArchiveData.listRootEntry.at(i).strFullPath == entry.strFullPath) { // 在第一层数据中找到entry,不添加数据 + stArchiveData.listRootEntry.removeAt(i); + break; + } + } - // 获取文件压缩包文件数目 - const auto nofEntries = zip_get_num_entries(archive, 0); + // 在第一层数据中没有找到entry,在第一层数据中添加entry + stArchiveData.listRootEntry.push_back(entry); + } - // 循环构建数据 - for (zip_int64_t i = 0; i < nofEntries; i++) { - if (QThread::currentThread()->isInterruptionRequested()) { - break; + // 更新压缩包map + stArchiveData.mapFileEntry.insert(entry.strFullPath, entry); } - - handleArchiveData(archive, i); // 构建数据 } - zip_close(archive); + stArchiveData.qComressSize = QFileInfo(m_strArchiveName).size(); // 更新压缩包大小 return PFT_Nomral; }