enifeder
enifeder

Reputation: 497

Android - Copy Files (all at the same time)

I am currently working on a basic file browser for android. I have a working version for copying files, however as it works its way through directories it copies the files it finds. I want to change that so that I can find the total size of all files before starting to copy, as to help with a better progress bar.

If there is another way to find the total size of a directory and all its contents?

Here is my current version. I am having trouble changing this, I have tried using an arrayList however when I try to copy the files at the end, I think they are trying to copy in the wrong order.

public void copyDirectory(File sourceLocation , File targetLocation) throws IOException {
        if (sourceLocation.isDirectory()) {
            if (!targetLocation.exists() && !targetLocation.mkdirs()) {
                throw new IOException("Cannot create directory: " + targetLocation.getAbsolutePath());
            }

            String[] children = sourceLocation.list();
            for (int i = 0; i < children.length; i++) {
                copyDirectory(new File(sourceLocation, children[i]),
                        new File(targetLocation, children[i]));
            }
        } else {                
            File directory = targetLocation.getParentFile();
            if (directory != null && !directory.exists() && !directory.mkdirs()) {
                throw new IOException("Cannot create directory: " + directory.getAbsolutePath());
            }

            FileInputStream in = new FileInputStream(sourceLocation);
            FileOutputStream out = new FileOutputStream(targetLocation);

            long fileLength = sourceLocation.length();

            byte[] buf = new byte[1024];
            long total = 0;
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
                total += len;
                publishProgress((int) (total * 100 / fileLength));
            }
            in.close();
            out.close();
        }
    }

Solution

jtwigg's answer should also work. I just thought I would add the solution I found. Can't answer my own question so I will put it here.

Looping through all the files in the directory and keeping a running total seems to work. Although it requires looping first for the size and again to actually copy the files. Just call getDirectorySize() with the file or directory you wish to copy before calling copyDirectory().

private void getDirectorySize(File sourceLocation) throws IOException {
        if (sourceLocation.isDirectory()) {
            String[] children = sourceLocation.list();
            for (int i = 0; i < children.length; i++) {
                getDirectorySize(new File(sourceLocation, children[i]));
            }
        } else {
            totalFileSize += sourceLocation.length();
        }
}

The function will require the global long totalFileSize, and then all that is required is to replace:

publishProgress((int) (total * 100 / fileLength));

with:

publishProgress((int) (total * 100 / totalFileSize));

Upvotes: 1

Views: 330

Answers (1)

twigg
twigg

Reputation: 146

If I understand you correctly, you wish to find the total size of all the files in the directory and then copy them. I would create another function, something like:

public void beginCopy(File source, File destination)
{
    ArrayList<PendingFile> filesToCopy = new ArrayList<PendingFile>();
    long totalSize = copyDirectory(source, destination, filesToCopy);
    // totalsize now contains the size of all the files
    // files to copy now contains a list of source and destination files

    // now modifying your copy method we can copy all the files
    long totalThusFar = 0;
    for (PendingFile pending : filesToCopy)
    {
        FileInputStream in = new FileInputStream(pending.source);
        FileOutputStream out = new FileOutputStream(pending.destination);

        long fileLength = sourceLocation.length();

        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
            totalThusFar += len;
            publishProgress((int) (total * 100 / totalsize));
        }
        in.close();
        out.close();
    }
}

you would need a PendingFile class/structure just to hold both source and destinations. You will add them to the ArrayList in your copy method like this:

public long copyDirectory(File sourceLocation , File targetLocation, ArrayList list) throws IOException {
    if (sourceLocation.isDirectory()) {
        if (!targetLocation.exists() && !targetLocation.mkdirs()) {
            throw new IOException("Cannot create directory: " + targetLocation.getAbsolutePath());
        }

        String[] children = sourceLocation.list();
        long totalSize = 0;
        for (int i = 0; i < children.length; i++) {
            totalSize += copyDirectory(new File(sourceLocation, children[i]),
                    new File(targetLocation, children[i]), list);
            return totalSize;
        }
    } else {                
        File directory = targetLocation.getParentFile();
        if (directory != null && !directory.exists() && !directory.mkdirs()) {
            throw new IOException("Cannot create directory: " + directory.getAbsolutePath());
        }

        list.add(new PendingFile(sourceLocation, targetLocation));
        return sourceLocation.length;
    }
}

I wrote all this just now so It probably won't work straight away but I think you should be able to get it working with this. Goodluck!

Upvotes: 2

Related Questions