我试图一次阻止多个文件,然后将它们复制到另一个位置。
应同时阻止源文件和目标文件。因此,我无法使用static QFile::copy()
功能。
要保留并移动由于QSharedPointer< QFile >
而使用QFile
的文件既不可复制也不可移动。
要完全执行整个操作,我使用QtConcurrent
框架。即:QtConcurrent::mappedReduced
和QFutureWatcher
。
要打开我使用 map 仿函数的所有文件对,然后按顺序复制它们我使用 reduce 仿函数。
using PFile = QSharedPointer< QFile >;
using PFileList = QList< PFile >;
template< typename Result, typename Functor >
struct FunctorWithResultType
: std::decay_t< Functor >
{
using result_type = Result;
FunctorWithResultType(Functor & functor)
: std::decay_t< Functor >{std::forward< Functor >(functor)}
{ ; }
};
template< typename Result, typename Functor >
FunctorWithResultType< Result, Functor >
addResultType(Functor && functor)
{
return {functor};
}
class Test
{
public :
QDir currentDirectory;
QFutureWatcher< PFileList > fileCopyFutureWatcher;
// ...
};
// ...
Test::Test()
{
auto onFilesCopied = [&]
{
PFileList copiedFiles = fileCopyFutureWatcher.result();
qCInfo(usbDevice) << "Files copy operation from drive finished.";
qCInfo(usbDevice) << copiedFiles.size();
for (PFile const & file : copiedFiles) {
//file->close(); // I want copiedFiles to be closed automatically at the end of current scope
}
};
connect(&fileCopyFutureWatcher, &fileCopyFutureWatcher.finished, onFilesCopied);
}
// ...
void Test::onDeviceAdded(QString deviceName)
{
qCInfo(usbDevice) << "USB device" << deviceName << "is added.";
if (!fileCopyFutureWatcher.isFinished()) {
return;
}
QDir sourceDirectory{deviceName};
if (!sourceDirectory.cd("dir")) {
qCInfo(usbDevice) << "Drive" << deviceName << "does not contain dir subdirectory.";
return;
}
auto destinationDirectory = currentDirectory;
if (!destinationDirectory.mkpath("fileCache")) {
qCCritical(usbDevice) << "Can't create fileCache subdirectory in"
<< destinationDirectory.absolutePath()
<< "directory";
return;
}
if (!destinationDirectory.cd("fileCache")) {
qCCritical(usbDevice) << "Can't change directory to fileCache subdirectory in"
<< destinationDirectory.absolutePath()
<< "directory";
return;
}
struct PFilePair
{
PFile source, destination;
};
auto openSourceAndDestinationFiles = [&, destinationDirectory] (QFileInfo const & fileInfo) -> PFilePair
{
auto source = PFile::create(fileInfo.absoluteFilePath());
QFile & sourceFile = *source;
if (!sourceFile.open(QFile::ReadOnly)) {
qCCritical(usbDevice) << "Can't open file" << sourceFile.fileName()
<< "to read:" << sourceFile.errorString();
return {};
}
auto destination = PFile::create(destinationDirectory.absoluteFilePath(fileInfo.fileName()));
QFile & destinationFile = *destination;;
if (!destinationFile.open(QFile::ReadWrite | QFile::Truncate)) {
qCCritical(usbDevice) << "Can't open file" << destinationFile.fileName()
<< "to write:" << destinationFile.errorString();
return {};
}
return {qMove(source), qMove(destination)};
};
auto copyFiles = [&] (PFileList & files, PFilePair const & filePair)
{
if (filePair.source.isNull() || filePair.destination.isNull()) {
return;
}
QFile & sourceFile = *filePair.source;
QFile & destinationFile = *filePair.destination;
qCInfo(usbDevice) << sourceFile.fileName() << "->" << destinationFile.fileName();
constexpr int size = (1 << 20); // 1MiB
QByteArray buffer{size, 0};
char * const data = buffer.data();
while (!sourceFile.atEnd()) {
auto bytesRead = sourceFile.read(data, size);
if (bytesRead < 0) {
qCCritical(usbDevice) << "Can't read file" << sourceFile.fileName()
<< ":" << sourceFile.errorString();
return;
}
auto bytesWritten = destinationFile.write(data, bytesRead);
while (bytesWritten < bytesRead) {
auto sizeWritten = destinationFile.write(data + bytesWritten, bytesRead - bytesWritten);
if (sizeWritten < 0) {
qCCritical(usbDevice) << "Can't write file" << destinationFile.fileName()
<< ":" << destinationFile.errorString();
return;
}
bytesWritten += sizeWritten;
}
Q_ASSERT(bytesWritten == bytesRead);
}
Q_ASSERT(sourceFile.size() == destinationFile.size());
destinationFile.flush();
files.append(filePair.destination);
};
QStringList nameFilters;
nameFilters << "file.dat";
// many other entries
auto entryInfoList = sourceDirectory.entryInfoList(nameFilters, (QDir::Readable | QDir::Files));
fileCopyFutureWatcher.setFuture(QtConcurrent::mappedReduced< PFileList >(qMove(entryInfoList), addResultType< PFilePair >(openSourceAndDestinationFiles), copyFiles));
}
// ...
在QFutureWatcher::finished
事件中读取结果后,我的应用程序仍会打开和阻止所有目标文件。因此,我可以得出结论,QSharedPointer< QFile >
的副本仍然存在。我怀疑他们离开QFuture
QFutureWatcher
。如何在不使用假QFile::~QFile()
实例调用QFutureWatcher::setFuture
的情况下清除所有这些文件(即如何使QFuture
关闭所有文件)?
要实现这一点,我需要从QFuture
窃取结果,而不是复制。
答案 0 :(得分:0)
我的解决方法如下:
using PFile = QSharedPointer< QFile >;
using PFileList = QList< PFile >;
template< typename Result, typename Functor >
struct FunctorWithResultType
: std::decay_t< Functor >
{
using result_type = Result;
FunctorWithResultType(Functor & functor)
: std::decay_t< Functor >{std::forward< Functor >(functor)}
{ ; }
};
template< typename Result, typename Functor >
FunctorWithResultType< Result, Functor >
addResultType(Functor && functor)
{
return {functor};
}
class Test
{
public :
QDir currentDirectory;
// ...
};
// ...
void Test::onDeviceAdded(QString deviceName)
{
qCInfo(usbDevice) << "USB device" << deviceName << "is added.";
QDir sourceDirectory{deviceName};
if (!sourceDirectory.cd("dir")) {
qCInfo(usbDevice) << "Drive" << deviceName << "does not contain dir subdirectory.";
return;
}
auto destinationDirectory = currentDirectory;
if (!destinationDirectory.mkpath("fileCache")) {
qCCritical(usbDevice) << "Can't create fileCache subdirectory in"
<< destinationDirectory.absolutePath()
<< "directory";
return;
}
if (!destinationDirectory.cd("fileCache")) {
qCCritical(usbDevice) << "Can't change directory to fileCache subdirectory in"
<< destinationDirectory.absolutePath()
<< "directory";
return;
}
struct PFilePair
{
PFile source, destination;
};
auto openSourceAndDestinationFiles = [&, destinationDirectory] (QFileInfo const & fileInfo) -> PFilePair
{
auto source = PFile::create(fileInfo.absoluteFilePath());
QFile & sourceFile = *source;
if (!sourceFile.open(QFile::ReadOnly)) {
qCCritical(usbDevice) << "Can't open file" << sourceFile.fileName()
<< "to read:" << sourceFile.errorString();
return {};
}
auto destination = PFile::create(destinationDirectory.absoluteFilePath(fileInfo.fileName()));
QFile & destinationFile = *destination;;
if (!destinationFile.open(QFile::ReadWrite | QFile::Truncate)) {
qCCritical(usbDevice) << "Can't open file" << destinationFile.fileName()
<< "to write:" << destinationFile.errorString();
return {};
}
return {qMove(source), qMove(destination)};
};
auto copyFiles = [&] (PFileList & files, PFilePair const & filePair)
{
if (filePair.source.isNull() || filePair.destination.isNull()) {
return;
}
QFile & sourceFile = *filePair.source;
QFile & destinationFile = *filePair.destination;
qCInfo(usbDevice) << sourceFile.fileName() << "->" << destinationFile.fileName();
constexpr int size = (1 << 20); // 1MiB
QByteArray buffer{size, 0};
char * const data = buffer.data();
while (!sourceFile.atEnd()) {
auto bytesRead = sourceFile.read(data, size);
if (bytesRead < 0) {
qCCritical(usbDevice) << "Can't read file" << sourceFile.fileName()
<< ":" << sourceFile.errorString();
return;
}
auto bytesWritten = destinationFile.write(data, bytesRead);
while (bytesWritten < bytesRead) {
auto sizeWritten = destinationFile.write(data + bytesWritten, bytesRead - bytesWritten);
if (sizeWritten < 0) {
qCCritical(usbDevice) << "Can't write file" << destinationFile.fileName()
<< ":" << destinationFile.errorString();
return;
}
bytesWritten += sizeWritten;
}
Q_ASSERT(bytesWritten == bytesRead);
}
Q_ASSERT(sourceFile.size() == destinationFile.size());
destinationFile.flush();
files.append(filePair.destination);
};
auto & fileCopyFutureWatcher = *new QFutureWatcher< PFileList >{this};
auto onFilesCopied = [&]
{
fileCopyFutureWatcher.deleteLater();
PFileList copiedFiles = fileCopyFutureWatcher.result();
qCInfo(usbDevice) << "Files copy operation from drive finished.";
qCInfo(usbDevice) << copiedFiles.size();
for (PFile const & file : copiedFiles) {
// ...
}
};
connect(&fileCopyFutureWatcher, &fileCopyFutureWatcher.finished, onFilesCopied);
QStringList nameFilters;
nameFilters << "*.dat";
auto entryInfoList = sourceDirectory.entryInfoList(nameFilters, (QDir::Readable | QDir::Files));
fileCopyFutureWatcher.setFuture(QtConcurrent::mappedReduced< PFileList >(qMove(entryInfoList), addResultType< PFilePair >(openSourceAndDestinationFiles), copyFiles));
}
// ...
我几乎可以肯定,fileCopyFutureWatcher
实例将delete
可靠。