Skip to content

Fix: keep renamed file content during compression#327

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:develop/snipefrom
GongHeng2017:202511211342-snipe-fix
Nov 21, 2025
Merged

Fix: keep renamed file content during compression#327
deepin-bot[bot] merged 1 commit intolinuxdeepin:develop/snipefrom
GongHeng2017:202511211342-snipe-fix

Conversation

@GongHeng2017
Copy link
Copy Markdown
Contributor

@GongHeng2017 GongHeng2017 commented Nov 21, 2025

  • copy aliased entries into a temp directory before invoking createArchive
  • update FileEntry paths to point at the real copies and clear aliases
  • cleanup the temp copies on success, failure, cancel, and reset
  • ensures libzip sees actual files instead of symlink paths

Log: fix bug
Bug: https://pms.uniontech.com/bug-view-340081.html

Summary by Sourcery

Handle renamed files during compression by copying aliased entries into real temporary files before archiving and cleaning them up afterwards

Bug Fixes:

  • Preserve renamed file and directory contents by copying actual files instead of using symlinks during compression

Enhancements:

  • Add prepareCompressAliasEntries and cleanupCompressAliasEntries to manage temporary copies and update FileEntry paths
  • Implement recursive directory copy utility and integrate it into the compression workflow
  • Ensure temporary alias entries are cleaned up on success, error, cancellation, and reset

- copy aliased entries into a temp directory before invoking createArchive
- update FileEntry paths to point at the real copies and clear aliases
- cleanup the temp copies on success, failure, cancel, and reset
- ensures libzip sees actual files instead of symlink paths

Log: fix bug
Bug: https://pms.uniontech.com/bug-view-340081.html
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Nov 21, 2025

Reviewer's Guide

Introduces a temporary copy mechanism for renamed files during compression by recursively copying aliased entries into a unique temp directory, updating FileEntry paths, clearing aliases, and ensuring cleanup on all completion paths.

Sequence diagram for compression with temporary alias file handling

sequenceDiagram
    participant MainWindow
    participant CompressPage
    participant FileSystem
    participant LibZip
    MainWindow->>CompressPage: getEntrys()
    CompressPage-->>MainWindow: listEntry
    MainWindow->>MainWindow: prepareCompressAliasEntries(listEntry)
    alt Aliased entries exist
        MainWindow->>FileSystem: Create temp directory
        MainWindow->>FileSystem: Copy aliased files/directories
        MainWindow->>MainWindow: Update FileEntry paths, clear aliases
    end
    MainWindow->>LibZip: createArchive(listEntry)
    alt On success/failure/cancel/reset
        MainWindow->>MainWindow: cleanupCompressAliasEntries()
        MainWindow->>FileSystem: Remove temp directory
    end
Loading

Class diagram for MainWindow and FileEntry changes

classDiagram
    class MainWindow {
        +QString m_strCompressAliasRoot
        +bool m_needCleanupCompressAlias
        +bool prepareCompressAliasEntries(QList<FileEntry>&)
        +void cleanupCompressAliasEntries()
    }
    class FileEntry {
        +QString strFullPath
        +QString strAlias
        +bool isDirectory
    }
    MainWindow "1" -- "*" FileEntry : manages
    MainWindow : prepareCompressAliasEntries(QList<FileEntry>&)
    MainWindow : cleanupCompressAliasEntries()
Loading

File-Level Changes

Change Details Files
Recursive directory‐copy utility to duplicate arbitrary folder trees
  • Added copyDirectoryRecursively function for deep file and folder copying
  • Ensured creation of target directories and removal of existing files before copy
src/source/mainwindow.cpp
prepareCompressAliasEntries and cleanupCompressAliasEntries methods to manage temp alias copies
  • Implemented prepareCompressAliasEntries to detect aliases, create a UUID‐based temp root, copy files/dirs, update FileEntry path, clear aliases, and set cleanup flag
  • Implemented cleanupCompressAliasEntries to recursively remove the temp alias directory and reset state
src/source/mainwindow.cpp
src/source/mainwindow.h
Integration of alias handling in compression workflow
  • Call prepareCompressAliasEntries at the start of slotCompress to create real file copies
  • Invoke cleanupCompressAliasEntries on compression success, error, cancellation, and reset to remove temp files
src/source/mainwindow.cpp
Extension of MainWindow class to track alias state
  • Added m_strCompressAliasRoot and m_needCleanupCompressAlias member variables
  • Declared prepareCompressAliasEntries and cleanupCompressAliasEntries in the class header
src/source/mainwindow.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

我来对这个diff进行详细的代码审查:

  1. 代码质量与结构:
  • 新增的匿名命名空间很好地封装了copyDirectoryRecursively函数,避免了全局命名空间污染
  • prepareCompressAliasEntries和cleanupCompressAliasEntries函数职责明确,分别处理临时文件的创建和清理
  • 在各个关键位置(压缩完成、取消、错误等)都添加了cleanupCompressAliasEntries调用,确保资源清理
  1. 安全性考虑:
  • 使用QUuid生成唯一的临时目录名,避免目录名冲突
  • 在创建临时目录和复制文件时都进行了错误检查
  • 使用removeRecursively确保临时目录被完整清理
  • 在错误发生时及时清理已创建的临时文件
  1. 性能优化建议:
  • copyDirectoryRecursively函数可以考虑使用QDirIterator而不是entryInfoList,这样可以避免一次性加载所有文件信息,对大型目录更友好
  • 可以考虑在复制大文件时添加进度反馈
  1. 错误处理改进:
  • 建议在copyDirectoryRecursively中添加更详细的错误日志,记录具体哪个文件/目录操作失败
  • 可以考虑添加磁盘空间检查,在开始复制前确认有足够空间
  1. 代码健壮性:
  • 建议在prepareCompressAliasEntries中添加对源文件是否存在的检查
  • 在文件复制时,可以添加权限检查,提前发现可能的权限问题
  1. 资源管理:
  • 建议使用RAII模式或QScopedPointer来管理临时目录的生命周期,这样更安全
  • 可以考虑使用QTemporaryDir替代手动创建和管理临时目录
  1. 具体修改建议:
// 改进copyDirectoryRecursively函数
bool copyDirectoryRecursively(const QString &sourcePath, const QString &targetPath) {
    QDir sourceDir(sourcePath);
    if (!sourceDir.exists()) {
        qWarning() << "Source directory does not exist:" << sourcePath;
        return false;
    }

    if (!QDir().mkpath(targetPath)) {
        qWarning() << "Failed to create target directory:" << targetPath;
        return false;
    }

    QDirIterator it(sourcePath, QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files | QDir::Hidden | QDir::System);
    while (it.hasNext()) {
        it.next();
        const QFileInfo &info = it.fileInfo();
        const QString srcFilePath = info.absoluteFilePath();
        const QString dstFilePath = targetPath + QDir::separator() + info.fileName();
        
        if (info.isDir()) {
            if (!copyDirectoryRecursively(srcFilePath, dstFilePath)) {
                return false;
            }
        } else {
            QFile::remove(dstFilePath);
            if (!QFile::copy(srcFilePath, dstFilePath)) {
                qWarning() << "Failed to copy file:" << srcFilePath << "to" << dstFilePath;
                return false;
            }
        }
    }
    return true;
}

// 在prepareCompressAliasEntries中添加源文件存在性检查
bool MainWindow::prepareCompressAliasEntries(QList<FileEntry> &listEntry) {
    m_needCleanupCompressAlias = false;
    m_strCompressAliasRoot.clear();

    bool hasAlias = false;
    QString aliasRoot;

    for (FileEntry &entry : listEntry) {
        if (entry.strAlias.isEmpty() || entry.strFullPath.isEmpty()) {
            continue;
        }

        // 检查源文件是否存在
        if (!QFileInfo::exists(entry.strFullPath)) {
            showWarningDialog(tr("Source file \"%1\" does not exist").arg(entry.strFullPath));
            return false;
        }

        if (!hasAlias) {
            // 使用QTemporaryDir管理临时目录
            m_tempDir.reset(new QTemporaryDir(TEMPPATH + QDir::separator() + m_strProcessID));
            if (!m_tempDir->isValid()) {
                showWarningDialog(tr("Failed to create temporary directory, please check and try again."));
                return false;
            }
            aliasRoot = m_tempDir->path() + QDir::separator() + QUuid::createUuid().toString(QUuid::WithoutBraces);
            if (!QDir().mkpath(aliasRoot)) {
                showWarningDialog(tr("Failed to create temporary directory, please check and try again."));
                return false;
            }
        }

        // 其余代码保持不变...
    }

    return true;
}
  1. 头文件建议:
    在mainwindow.h中添加:
#include <QScopedPointer>
#include <QTemporaryDir>

在private成员中添加:

QScopedPointer<QTemporaryDir> m_tempDir;

这些改进将使代码更健壮、更安全,同时提供更好的错误处理和资源管理。

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • Consider using QTemporaryDir for your alias root instead of manually generating and cleaning up the temp directory, which will simplify lifecycle management and avoid orphaned dirs.
  • The multiple scattered calls to cleanupCompressAliasEntries could be replaced with an RAII guard or a single ‘finally’‐style handler to ensure cleanup always happens without duplicating calls.
  • Since copyDirectoryRecursively can take time on large file trees, you might want to move it off the UI thread or add progress feedback to prevent UI freezing during the copy step.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider using QTemporaryDir for your alias root instead of manually generating and cleaning up the temp directory, which will simplify lifecycle management and avoid orphaned dirs.
- The multiple scattered calls to cleanupCompressAliasEntries could be replaced with an RAII guard or a single ‘finally’‐style handler to ensure cleanup always happens without duplicating calls.
- Since copyDirectoryRecursively can take time on large file trees, you might want to move it off the UI thread or add progress feedback to prevent UI freezing during the copy step.

## Individual Comments

### Comment 1
<location> `src/source/mainwindow.cpp:2424-2427` </location>
<code_context>
+    }
+
+    QDir aliasRoot(m_strCompressAliasRoot);
+    if (aliasRoot.exists()) {
+        aliasRoot.removeRecursively();
+    }
+    m_strCompressAliasRoot.clear();
</code_context>

<issue_to_address>
**suggestion (bug_risk):** removeRecursively may fail silently if files are locked or permissions are insufficient.

Check the result of removeRecursively and handle failures, such as logging or retrying, to prevent leftover files.

```suggestion
    if (aliasRoot.exists()) {
        bool removed = aliasRoot.removeRecursively();
        if (!removed) {
            qWarning() << "Failed to remove alias root directory recursively:" << m_strCompressAliasRoot;
            // Optionally, you could retry or set a flag here if needed
        }
    }
    m_strCompressAliasRoot.clear();
```
</issue_to_address>

### Comment 2
<location> `src/source/mainwindow.cpp:2345` </location>
<code_context>
     }
 }

+bool MainWindow::prepareCompressAliasEntries(QList<FileEntry> &listEntry)
+{
+    m_needCleanupCompressAlias = false;
</code_context>

<issue_to_address>
**issue (complexity):** Consider refactoring manual temp directory and cleanup logic into a single RAII helper class to centralize resource management and reduce code duplication.

```markdown
You can collapse all of that manual‐temp‐dir and cleanup logic into one RAII helper, and move `copyDirectoryRecursively` into a small “file-utils” unit.  Here’s one way:

1.  Create a helper class in its own files (e.g. CompressAliasHelper.h/.cpp):

```cpp
// CompressAliasHelper.h
#pragma once
#include <QTemporaryDir>
#include <QList>
#include "FileEntry.h"   // your struct
class CompressAliasHelper {
public:
  explicit CompressAliasHelper(const QString &processId);
  ~CompressAliasHelper();            // auto-cleans on destruction
  bool prepare(QList<FileEntry> &entries);
private:
  QTemporaryDir m_tempRoot;
  QString        m_uuidRoot;         // e.g. /tmp/.../compress_alias/UUID
  bool           makeTarget(const QString &sub, QString &outPath);
};
```

```cpp
// CompressAliasHelper.cpp
#include "CompressAliasHelper.h"
#include <QDirIterator>
#include <QUuid>
#include <QFile>
#include <QDir>
#include <QDebug>

CompressAliasHelper::CompressAliasHelper(const QString &pid)
  : m_tempRoot(QDir::tempPath() + QDir::separator() + pid + QDir::separator() + "compress_alias")
{
  if (m_tempRoot.isValid()) {
    m_uuidRoot = m_tempRoot.path() + QDir::separator()
                + QUuid::createUuid().toString(QUuid::WithoutBraces);
    QDir().mkpath(m_uuidRoot);
  }
}

CompressAliasHelper::~CompressAliasHelper() = default;

static bool copyDirectoryRecursively(const QString &src, const QString &dst) {
  QDir source(src);
  if (!source.exists()) return false;
  QDir target;
  if (!target.mkpath(dst)) return false;
  for (auto &info : source.entryInfoList(
         QDir::NoDotAndDotDot|QDir::AllDirs|QDir::Files|QDir::Hidden|QDir::System)) {
    QString to = dst + QDir::separator() + info.fileName();
    if (info.isDir()) {
      if (!copyDirectoryRecursively(info.absoluteFilePath(), to))
        return false;
    } else {
      QFile::remove(to);
      if (!QFile::copy(info.absoluteFilePath(), to))
        return false;
    }
  }
  return true;
}

bool CompressAliasHelper::makeTarget(const QString &sub, QString &outPath) {
  outPath = m_uuidRoot + QDir::separator() + sub;
  return QDir().mkpath(QFileInfo(outPath).path());
}

bool CompressAliasHelper::prepare(QList<FileEntry> &entries) {
  if (!m_tempRoot.isValid()) return false;

  bool any = false;
  for (auto &e : entries) {
    if (e.strAlias.isEmpty()) continue;
    QString tgt;
    if (!makeTarget(e.strAlias, tgt)) return false;

    bool ok = e.isDirectory
      ? copyDirectoryRecursively(e.strFullPath, tgt)
      : QFile::copy(e.strFullPath, tgt);
    if (!ok) return false;

    e.strFullPath = tgt;
    e.strAlias.clear();
    any = true;
  }
  return true;
}
```

2.  In `MainWindow`, simply:

```cpp
void MainWindow::onStartCompress() {
  QList<FileEntry> files = m_pCompressPage->getEntrys();
  CompressAliasHelper alias(m_strProcessID);
  if (!alias.prepare(files)) {
    showWarningDialog(tr("Failed to prepare renamed items"));
    return;
  }
  // … now use `files` and let alias’s destructor auto‐clean…
}
```

3.  Remove all the scattered `cleanupCompressAliasEntries()` calls.  
4.  Move the free `copyDirectoryRecursively` into your new `CompressAliasHelper.cpp` (or a shared `FileUtils.cpp`).

This preserves behavior but removes:
- manual `m_needCleanupCompressAlias` flags  
- repeated `cleanupCompressAliasEntries()` calls  
- deep nesting and duplication in `MainWindow`  
- keeps temp dirs auto‐removed via `QTemporaryDir`’s RAII destructor.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: GongHeng2017, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@GongHeng2017
Copy link
Copy Markdown
Contributor Author

/merge

@deepin-bot deepin-bot bot merged commit 7b7c20e into linuxdeepin:develop/snipe Nov 21, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants