From 10718d1dafc03f0a15b6e6a6b6c67fa2a0fbc006 Mon Sep 17 00:00:00 2001 From: zhangtingan Date: Wed, 23 Sep 2026 13:57:06 +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 +++++++++++++++++++++---- src/source/mainwindow.cpp | 20 ++- src/source/mainwindow.h | 1 + 3 files changed, 161 insertions(+), 25 deletions(-) diff --git a/3rdparty/libzipplugin/libzipplugin.cpp b/3rdparty/libzipplugin/libzipplugin.cpp index 4b360db32..2aaf63450 100644 --- a/3rdparty/libzipplugin/libzipplugin.cpp +++ b/3rdparty/libzipplugin/libzipplugin.cpp @@ -545,39 +545,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; } diff --git a/src/source/mainwindow.cpp b/src/source/mainwindow.cpp index fe0b3d4cd..797a4160d 100644 --- a/src/source/mainwindow.cpp +++ b/src/source/mainwindow.cpp @@ -1556,6 +1556,7 @@ void MainWindow::handleJobNormalFinished(ArchiveJob::JobType eType, ErrorType eE // 追加/删除更新 case ArchiveJob::JT_Update: { qInfo() << "更新结束"; + m_bInternalArchiveOperation = false; m_pLoadingPage->stopLoading(); // 停止更新 if (DataManager::get_instance().archiveData().listRootEntry.count() == 0) { @@ -1606,6 +1607,7 @@ void MainWindow::handleJobCancelFinished(ArchiveJob::JobType eType) break; // 添加文件至压缩包 case ArchiveJob::JT_Add: { + m_bInternalArchiveOperation = false; //拖拽追加取消后不需要返回列表界面 if (StartupType::ST_DragDropAdd != m_eStartupType) { m_ePageID = PI_UnCompress; @@ -1650,6 +1652,13 @@ void MainWindow::handleJobCancelFinished(ArchiveJob::JobType eType) break; // 删除 case ArchiveJob::JT_Delete: { + m_bInternalArchiveOperation = false; + m_ePageID = PI_UnCompress; + } + break; + // 重命名 + case ArchiveJob::JT_Rename: { + m_bInternalArchiveOperation = false; m_ePageID = PI_UnCompress; } break; @@ -1707,6 +1716,7 @@ void MainWindow::handleJobErrorFinished(ArchiveJob::JobType eJobType, ErrorType break; // 压缩包追加文件错误 case ArchiveJob::JT_Add: { + m_bInternalArchiveOperation = false; //拖拽追加失败后需要跳转到失败界面, if (StartupType::ST_DragDropAdd != m_eStartupType) { m_ePageID = PI_UnCompress; @@ -1869,6 +1879,7 @@ void MainWindow::handleJobErrorFinished(ArchiveJob::JobType eJobType, ErrorType break; // 删除错误 case ArchiveJob::JT_Delete: { + m_bInternalArchiveOperation = false; m_ePageID = PI_UnCompress; #if 0 // 删除错误提示暂时不需要 QIcon icon = UiTools::renderSVG(":assets/icons/deepin/builtin/icons/compress_fail_128px.svg", QSize(30, 30)); @@ -1947,7 +1958,7 @@ void MainWindow::handleJobErrorFinished(ArchiveJob::JobType eJobType, ErrorType break; // 更新压缩包数据错误 case ArchiveJob::JT_Update: - + m_bInternalArchiveOperation = false; break; default: break; @@ -2003,6 +2014,7 @@ void MainWindow::addFiles2Archive(const QStringList &listFiles, const QString &s void MainWindow::resetMainwindow() { + m_bInternalArchiveOperation = false; #ifdef __aarch64__ maxFileSize_ = 0; #endif @@ -2529,6 +2541,7 @@ void MainWindow::watcherArchiveFile(const QString &strFullPath) m_pFileWatcher->startWatcher(); connect(m_pFileWatcher, &DFileWatcher::fileMoved, this, [ = ]() { //监控压缩包,重命名时提示 + if (m_bInternalArchiveOperation) return; // 取消操作 slotCancel(); @@ -2542,6 +2555,7 @@ void MainWindow::watcherArchiveFile(const QString &strFullPath) }); connect(m_pFileWatcher, &DFileWatcher::fileDeleted, this, [ = ]() { //监控压缩包,重命名时提示 + if (m_bInternalArchiveOperation) return; QTimer::singleShot(1000, this, [=]() { if(QFile::exists(strFullPath)) { @@ -3091,6 +3105,7 @@ void MainWindow::slotDelFiles(const QList &listSelEntry, qint64 qTota qInfo() << "删除文件:"; m_operationtype = Operation_DELETE; //提取操作 QString strArchiveFullPath = m_pUnCompressPage->archiveFullPath(); + m_bInternalArchiveOperation = true; if (ArchiveManager::get_instance()->deleteFiles(strArchiveFullPath, listSelEntry)) { // 设置更新选项 m_stUpdateOptions.reset(); @@ -3144,6 +3159,7 @@ void MainWindow::slotRenameFile(const FileEntry &SelEntry, qint64 qTotalSize) return; } QString strArchiveFullPath = m_pUnCompressPage->archiveFullPath(); + m_bInternalArchiveOperation = true; if (ArchiveManager::get_instance()->renameFiles(strArchiveFullPath, sListEntry)) { // 设置更新选项 m_stUpdateOptions.reset(); @@ -3191,6 +3207,7 @@ void MainWindow::slotOpenFile(const FileEntry &entry, const QString &strProgram) void MainWindow::slotOpenFileChanged(const QString &strPath) { + if (m_bInternalArchiveOperation) return; QMap &mapStatus = m_pOpenFileWatcher->getFileHasModified(); QMap mapPassword = m_pOpenFileWatcher->getFilePassword(); qInfo() << strPath; @@ -3621,6 +3638,7 @@ void MainWindow::slotFinishCalculateSize(qint64 size, QString strArchiveFullPath } } else { // 调用添加文件接口 + m_bInternalArchiveOperation = true; if (ArchiveManager::get_instance()->addFiles(strArchiveFullPath, listAddEntry, stOptions)) { // 切换进度界面 m_pProgressPage->setTotalSize(size); diff --git a/src/source/mainwindow.h b/src/source/mainwindow.h index 9aa7f2a05..2c4e603e3 100644 --- a/src/source/mainwindow.h +++ b/src/source/mainwindow.h @@ -584,6 +584,7 @@ private Q_SLOTS: bool m_isFirstViewComment = true; DFileWatcher *m_pFileWatcher = nullptr; // 文件监控 + bool m_bInternalArchiveOperation = false; // 内部操作(重命名/删除/追加)进行中,抑制文件监控 QString m_strFinalConvertFile; // 格式转换最终的文件全路径 QString m_fileWriteErrorName; // 创建失败的文件名