Copy directory using Qt
Asked Answered
U

10

26

I want to copy a directory from one drive to another drive. My selected directory contains many sub directories and files.

How can I implement the same using Qt?

Unparalleled answered 29/3, 2010 at 8:11 Comment(0)
I
29
void copyPath(QString src, QString dst)
{
    QDir dir(src);
    if (! dir.exists())
        return;

    foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
        QString dst_path = dst + QDir::separator() + d;
        dir.mkpath(dst_path);
        copyPath(src+ QDir::separator() + d, dst_path);
    }

    foreach (QString f, dir.entryList(QDir::Files)) {
        QFile::copy(src + QDir::separator() + f, dst + QDir::separator() + f);
    }
}
Interception answered 7/8, 2013 at 0:15 Comment(2)
I don't know if it's my rig, or this code in Qt 5.6, but this code seems to hang. It's doing directory-copying, but never finishing. Weird.Manchester
Hm, yep. First copy operation works, and then subsequent copies are going bananas. Make a dir, then copy that dir into a subdir. First works, second, bang, dead.Manchester
P
13

Manually, you can do the next things:

1). with func below you generate folders/files list (recursively) - the destination files.

static void recurseAddDir(QDir d, QStringList & list) {

    QStringList qsl = d.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);

    foreach (QString file, qsl) {

        QFileInfo finfo(QString("%1/%2").arg(d.path()).arg(file));

        if (finfo.isSymLink())
            return;

        if (finfo.isDir()) {

            QString dirname = finfo.fileName();
            QDir sd(finfo.filePath());

            recurseAddDir(sd, list);

        } else
            list << QDir::toNativeSeparators(finfo.filePath());
    }
}

2). then you may to start copying files from destination list to the new source directory like that:

for (int i = 0; i < gtdStringList.count(); i++) {

    progressDialog.setValue(i);
    progressDialog.setLabelText(tr("%1 Coping file number %2 of %3 ")
        .arg((conf->isConsole) ? tr("Making copy of the Alta-GTD\n") : "")
        .arg(i + 1)
        .arg(gtdStringList.count()));

    qApp->processEvents(QEventLoop::ExcludeUserInputEvents);

    if (progressDialog.wasCanceled()) {

        // removing tmp files/folders
        rmDirectoryRecursive(tmpFolder);
        rmDirectoryRecursive(tmpFolderPlus);
        setEnableGUI(true);
        return;
    }

    // coping
    if (!QFile::copy(gtdStringList.at(i), tmpStringList.at(i))) {

        if (warningFlag) {

            QMessageBox box(this);
            QString name = tr("Question");
            QString file1 = getShortName(gtdStringList.at(i), QString("\\...\\"));
            QString file2 = getShortName(tmpStringList.at(i), QString("\\...\\"));
            QString text = tr("Cannot copy <b>%1</b> <p>to <b>%2</b>"   \
               "<p>This file will be ignored, just press <b>Yes</b> button" \
               "<p>Press <b>YesToAll</b> button to ignore other warnings automatically..."  \
               "<p>Or press <b>Abort</b> to cancel operation").arg(file1).arg(file2);

            box.setModal(true);
            box.setWindowTitle(name);
            box.setText(QString::fromLatin1("%1").arg(text));
            box.setIcon(QMessageBox::Question);
            box.setStandardButtons(QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::Abort);

            switch (box.exec()) {                   
                case (QMessageBox::YesToAll):
                    warningFlag = false;
                    break;
                case (QMessageBox::Yes):
                    break;
                case (QMessageBox::Abort):
                    rmDirectoryRecursive(tmpFolder);
                    rmDirectoryRecursive(tmpFolderPlus);
                    setEnableGUI(true);
                    return;
            }
        }
    }
}

And that's all. Good luck!

Pushing answered 29/3, 2010 at 8:50 Comment(4)
i've tried posting my correction as an edit but nobody seems to understand it, so i'll post it here. using QString("%1/%2").arg(d.path()).arg(file) is generally not a good idea seeing as '%1' or '%2' can be found (in most filesystems) in a file or path name. take this path created by cygwin as an example c:\cyg\ftp%3a%2f%2fcygwin.mirrorcatalogs.com%2fcygwin%2f.Julejulee
lets say this is what is held in d.path() and file has text.txt. '%1' will be replaced by d.path() to form c:\cyg\ftp%3a%2f%2fcygwin.mirrorcatalogs.com%2fcygwin%2f/%2. finally you'll have c:\cyg\ftp%3atext.txtftext.txtfcygwin.mirrorcatalogs.comtext.txtfcygwintext.txtf/text.txt. a better option is d.path().append('/').append(file)Julejulee
If you use append it return a reference and if you use it again in other place it may change.Be careful if desidedAriew
The safe alternative is QString("%1/%2").arg(d.path(), file) - but it's better to compose pathnames using d.filePath(file) which will use the platform's directory separator portably. You still need the QString::arg(QString, QString) for composing the message box text, of course.Poised
E
8

I wanted something similar, and was googling (in vain), so this is where I've got to:

static bool cpDir(const QString &srcPath, const QString &dstPath)
{
    rmDir(dstPath);
    QDir parentDstDir(QFileInfo(dstPath).path());
    if (!parentDstDir.mkdir(QFileInfo(dstPath).fileName()))
        return false;

    QDir srcDir(srcPath);
    foreach(const QFileInfo &info, srcDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
        QString srcItemPath = srcPath + "/" + info.fileName();
        QString dstItemPath = dstPath + "/" + info.fileName();
        if (info.isDir()) {
            if (!cpDir(srcItemPath, dstItemPath)) {
                return false;
            }
        } else if (info.isFile()) {
            if (!QFile::copy(srcItemPath, dstItemPath)) {
                return false;
            }
        } else {
            qDebug() << "Unhandled item" << info.filePath() << "in cpDir";
        }
    }
    return true;
}

It uses an rmDir function that looks pretty similar:

static bool rmDir(const QString &dirPath)
{
    QDir dir(dirPath);
    if (!dir.exists())
        return true;
    foreach(const QFileInfo &info, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
        if (info.isDir()) {
            if (!rmDir(info.filePath()))
                return false;
        } else {
            if (!dir.remove(info.fileName()))
                return false;
        }
    }
    QDir parentDir(QFileInfo(dirPath).path());
    return parentDir.rmdir(QFileInfo(dirPath).fileName());
}

This doesn't handle links and special files, btw.

Evangelist answered 31/3, 2011 at 13:1 Comment(0)
H
7

The hard way. Copy every file individually.

  • Use QDir::entryList() to iterate over the content of a directory
  • Use QDir::cd() and QDir::cdUp() to go in and out of directories
  • Use QDir::mkdir() and QDir::mkpath() to create the new folders tree
  • and finally, use QFile::copy() to copy the actual files
Hooves answered 29/3, 2010 at 8:22 Comment(0)
M
3

This is basically petch's answer with a slight change due to it breaking for me in Qt 5.6 (this is the top question hit), so all credit goes to petch.

function

bool copyPath(QString sourceDir, QString destinationDir, bool overWriteDirectory)
{
    QDir originDirectory(sourceDir);

    if (! originDirectory.exists())
    {
        return false;
    }

    QDir destinationDirectory(destinationDir);

    if(destinationDirectory.exists() && !overWriteDirectory)
    {
        return false;
    }
    else if(destinationDirectory.exists() && overWriteDirectory)
    {
        destinationDirectory.removeRecursively();
    }

    originDirectory.mkpath(destinationDir);

    foreach (QString directoryName, originDirectory.entryList(QDir::Dirs | \
                                                              QDir::NoDotAndDotDot))
    {
        QString destinationPath = destinationDir + "/" + directoryName;
        originDirectory.mkpath(destinationPath);
        copyPath(sourceDir + "/" + directoryName, destinationPath, overWriteDirectory);
    }

    foreach (QString fileName, originDirectory.entryList(QDir::Files))
    {
        QFile::copy(sourceDir + "/" + fileName, destinationDir + "/" + fileName);
    }

    /*! Possible race-condition mitigation? */
    QDir finalDestination(destinationDir);
    finalDestination.refresh();

    if(finalDestination.exists())
    {
        return true;
    }

    return false;
}

Use:

/*! Overwrite existing directories. */
bool directoryCopied = copyPath(sourceDirectory, destinationDirectory, true);

/*! Do not overwrite existing directories. */
bool directoryCopied = copyPath(sourceDirectory, destinationDirectory, false);
Manchester answered 6/4, 2016 at 19:33 Comment(1)
What's different and why?Poised
P
3

Try this:

bool copyDirectoryFiles(const QString &fromDir, const QString &toDir, bool coverFileIfExist)
{
    QDir sourceDir(fromDir);
    QDir targetDir(toDir);
    if(!targetDir.exists()){    /* if directory don't exists, build it */
        if(!targetDir.mkdir(targetDir.absolutePath()))
            return false;
    }

    QFileInfoList fileInfoList = sourceDir.entryInfoList();
    foreach(QFileInfo fileInfo, fileInfoList){
        if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
            continue;

        if(fileInfo.isDir()){    /* if it is directory, copy recursively*/
            if(!copyDirectoryFiles(fileInfo.filePath(),  
                targetDir.filePath(fileInfo.fileName()), 
                coverFileIfExist)) 
                return false;
        }
        else{            /* if coverFileIfExist == true, remove old file first */
            if(coverFileIfExist && targetDir.exists(fileInfo.fileName())){
                targetDir.remove(fileInfo.fileName());
            }

            // files copy
            if(!QFile::copy(fileInfo.filePath(),  
                targetDir.filePath(fileInfo.fileName()))){ 
                    return false;
            }
        }
    }
    return true;
}
Pallas answered 20/5, 2016 at 1:0 Comment(1)
Thank you for this code snippet, which may provide some immediate help. A proper explanation would greatly improve its educational value by showing why this is a good solution to the problem, and would make it more useful to future readers with similar, but not identical, questions. Please edit your answer to add explanation, and give an indication of what limitations and assumptions apply.Poised
C
1

I have made a library to manipulate files by a shell command style API. It supports a recursively copy of files and handled several more conditions.

https://github.com/benlau/qtshell#cp

Example

cp("-a", ":/*", "/target"); // copy all files from qrc resource to target path recursively

cp("tmp.txt", "/tmp");

cp("*.txt", "/tmp");

cp("/tmp/123.txt", "456.txt");

cp("-va","src/*", "/tmp");

cp("-a", ":resource","/target");
Cedillo answered 1/3, 2017 at 13:5 Comment(0)
M
0

Since I had some trouble with App-Bundles on macOS, here's a solution with QDirIterator

void copyAndReplaceFolderContents(const QString &fromDir, const QString &toDir, bool copyAndRemove = false) {
QDirIterator it(fromDir, QDirIterator::Subdirectories);
QDir dir(fromDir);
const int absSourcePathLength = dir.absoluteFilePath(fromDir).length();

while (it.hasNext()){
    it.next();
    const auto fileInfo = it.fileInfo();
    if(!fileInfo.isHidden()) { //filters dot and dotdot
        const QString subPathStructure = fileInfo.absoluteFilePath().mid(absSourcePathLength);
        const QString constructedAbsolutePath = toDir + subPathStructure;
        
        if(fileInfo.isDir()){
            //Create directory in target folder
            dir.mkpath(constructedAbsolutePath);
        } else if(fileInfo.isFile()) {
            //Copy File to target directory

            //Remove file at target location, if it exists. Otherwise QFile::copy will fail
            QFile::remove(constructedAbsolutePath);
            QFile::copy(fileInfo.absoluteFilePath(), constructedAbsolutePath);
        }
    }
}

if(copyAndRemove)
    dir.removeRecursively();

}

Musjid answered 18/3, 2021 at 9:59 Comment(0)
T
0

If you are on a linux based system and the cp command exists and can be run, then you can use a QProcess to launch a bash:

auto copy = new QProcess(this);
copy->start(QStringLiteral("cp -rv %1 %2").arg(sourceFolder, destinationFolder));
copy->waitForFinished();
copy->close();

cp details:

  • -r means recursively
  • -v means it prints the successfully copied file

Note: if the copy operation is long, then you need to managed the UI freezing, as noted here

Tansey answered 26/8, 2021 at 8:57 Comment(0)
M
0
  • Assuming the target location is empty "no existing files or folders with the same names" and you have no problem to use a Recursive function!! to copy a directory recursively then the code will be something like this

void copy_all(QString dst_loc, QString src_loc)
{
    QDir src(src_loc);
    for(QFileInfo file_info : src.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)){
        if(file_info.isDir()){
            src.mkpath(dst_loc+'/'+file_info.fileName());
            copy_all(dst_loc+'/'+file_info.fileName(),file_info.absoluteFilePath());
        }
        QFile::copy(file_info.absoluteFilePath(), dst_loc+'/'+file_info.fileName());
    }
}

if you ever dealt with tree data structures and tried to create a Recursive function to do a "depth-first search" Alogorithm you will get a 85% similar algorithm, which Actually I got this idea from.

  • The second way, is by using a map data structure to hold the current fileInfoList in a directory, and the corresponding state to show if you have used this fileInfo or not yet. and you gather all information firstly about sub directories and files from the source location.

And this is how most OS and other file managers do to copy data, by showing you the size of files to be copied, how many files and folders are going to be copied, and finally, if there is any conflict of files or folders with the same name before you even initiate copying "if you will do the same algorithm with the destination so that you can match filenames".

Good luck!.

Mclendon answered 9/7, 2022 at 7:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.