34

I am downloading files from the internet and saving the streaming data to a temp file in my app's internal storage given by getFilesDir().

Once the download is complete, I need to move the temp file to my download directory on External Memory (usually an SD Card). For some reason though, File.renameTo() isn't working for this. I'm guessing there's a problem because it's two separate file systems, but I can still download directly to the SD Card and the file URIs are correct.

Is there another simple and quick way to transfer that file from internal memory to external or do I have to do a byte stream copy and delete the original?

CodeFusionMobile
  • 14,812
  • 25
  • 102
  • 140
  • old question, but to answer why file.renameTo() didn't work, it is because the function requires the locations to be on the same mount-point, and sdcard and internal are different locations: "Both paths be on the same mount point. On Android, applications are most likely to hit this restriction when attempting to copy between internal storage and an SD card." https://developer.android.com/reference/java/io/File.html#renameTo(java.io.File) – mix3d Aug 01 '16 at 19:04
  • 2020, `File file = new File(getExternalFilesDir(null), fileName + ".mp3");` already gives the internal storage – dp2050 Mar 01 '20 at 06:29

8 Answers8

89

To copy files from internal memory to SD card and vice-versa using following piece of code:

public static void copyFile(File src, File dst) throws IOException
{
    FileChannel inChannel = new FileInputStream(src).getChannel();
    FileChannel outChannel = new FileOutputStream(dst).getChannel();
    try
    {
        inChannel.transferTo(0, inChannel.size(), outChannel);
    }
    finally
    {
        if (inChannel != null)
            inChannel.close();
        if (outChannel != null)
            outChannel.close();
    }
}

And - it works...

Barmaley
  • 16,638
  • 18
  • 73
  • 146
  • 7
    Make sure you have the following permissions if writing/reading external memory – Jon Willis Feb 17 '11 at 06:33
  • 1
    Shouldn't constructors be inside try-block? – ironic Dec 23 '13 at 14:12
  • Don't you have to close the results from `new FileInputStream(src)` and `new FileOutputStream(dst)` which don't even have a reference here? – caw Nov 21 '14 at 00:05
  • Nope, closing of channel closes those streams – Barmaley Nov 21 '14 at 08:23
  • @barmaley: What about folder?? Instead of file. I have 3 folders and i want to cut-paste it to new location. But it shows an error at line `FileChannel inChannel = new FileInputStream(src).getChannel();` saying filenotfound exception. though file exists. :( Any solution to this? – Mitesh Shah Dec 16 '14 at 11:54
  • I tried your code snippet, and I stacked with the issue, I get `java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_201423.jpg: open failed: EISDIR (Is a directory)` when I arrive at the line `outChannel = new FileOutputStream(dst).getChannel();`. I checked the value of `dst` variable, and it has the value `/sdcard/AppProj/IMG_20150626_201423.jpg`, so why I get this exception and how to fix it? I have ``. Android API 15 Platform. – Mike Jun 26 '15 at 17:19
  • I have a problem with FileChannel.size(), it returns 0, with small files. Files ~ 10Mb in size do not have this problem. – ODAXY Jan 24 '18 at 10:13
12

Internal and external memory is two different file systems. Therefore renameTo() fails.

You will have to copy the file and delete the original

Jacob Nordfalk
  • 3,533
  • 1
  • 21
  • 21
10

After you copy the file (as @barmaley's great answer shows), don't forget to expose it to the device's gallery, so the user can view it later.

The reason why it has to be done manually is that

Android runs a full media scan only on reboot and when (re)mounting the SD card

(as this guide shows).

The easier way to do this is by sending a broadcast for the scanning to be invoked:

Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(outputFile));
context.sendBroadcast(intent);

And voila! You can now view your file in the device's gallery.

Community
  • 1
  • 1
limlim
  • 3,115
  • 2
  • 34
  • 46
4

An alternative to the copying using your own function is to use Apache's library's class "FileUtils" , in the function called copyFile :

FileUtils.copyFile(src, dst, true);
android developer
  • 114,585
  • 152
  • 739
  • 1,270
0

Did some trivial modifications to @barmaley's code

public boolean copyFile(File src, File dst) {
    boolean returnValue = true;

   FileChannel inChannel = null, outChannel = null;

    try {

        inChannel = new FileInputStream(src).getChannel();
        outChannel = new FileOutputStream(dst).getChannel();

   } catch (FileNotFoundException fnfe) {

        Log.d(logtag, "inChannel/outChannel FileNotFoundException");
        fnfe.printStackTrace();
        return false;
   }

   try {
       inChannel.transferTo(0, inChannel.size(), outChannel);

   } catch (IllegalArgumentException iae) {

         Log.d(logtag, "TransferTo IllegalArgumentException");
         iae.printStackTrace();
         returnValue = false;

   } catch (NonReadableChannelException nrce) {

         Log.d(logtag, "TransferTo NonReadableChannelException");
         nrce.printStackTrace();
         returnValue = false;

   } catch (NonWritableChannelException nwce) {

        Log.d(logtag, "TransferTo NonWritableChannelException");
        nwce.printStackTrace();
        returnValue = false;

   } catch (ClosedByInterruptException cie) {

        Log.d(logtag, "TransferTo ClosedByInterruptException");
        cie.printStackTrace();
        returnValue = false;

   } catch (AsynchronousCloseException ace) {

        Log.d(logtag, "TransferTo AsynchronousCloseException");
        ace.printStackTrace();
        returnValue = false;

   } catch (ClosedChannelException cce) {

        Log.d(logtag, "TransferTo ClosedChannelException");
        cce.printStackTrace(); 
        returnValue = false;

    } catch (IOException ioe) {

        Log.d(logtag, "TransferTo IOException");
        ioe.printStackTrace();
        returnValue = false;


    } finally {

         if (inChannel != null)

            try {

               inChannel.close();
           } catch (IOException e) {
               e.printStackTrace();
           }

        if (outChannel != null)
            try {
                outChannel.close();
           } catch (IOException e) {
                e.printStackTrace();
           }

        }

       return returnValue;
    }
user2903200
  • 708
  • 2
  • 7
  • 19
  • 2
    Do you have any reasons or commentary for the changes? Why is this an improvement? – James A Mohler Oct 22 '13 at 16:59
  • Old answer, but I have to caution that this *is worse* for the modifications. It's worse because it actively hides error conditions. It may appear convenient, but utility functions are not in a position to know whether callers need to distinguish between FileNotFound, NonWritableChannel, etc. It makes downstream logic necessarily fuzzy. – Ben Aug 26 '15 at 19:22
0

Picture that:

  • This is internal path : pathInternal
  • And this is external path : pathExternal

Try with this code:

public void moveIn (String pathInternal, String pathExternal) {
    File fInternal = new File (pathInternal);
    File fExternal = new File (pathExternal);
    if (fInternal.exists()) {
        fInternal.renameTo(fExternal);
    }
}
Son Nguyen Thanh
  • 1,199
  • 15
  • 19
  • 1
    This does not work, since "Both paths be on the same mount point. On Android, applications are most likely to hit this restriction when attempting to copy between internal storage and an SD card." https://developer.android.com/reference/java/io/File.html#renameTo(java.io.File) – mix3d Aug 01 '16 at 19:03
0

You can do it using operations with byte[]

define in your class:

    public static final String DATA_PATH = 
Environment.getExternalStorageDirectory().toString() + "/MyAppName/";

then:

AssetManager assetManager = context.getAssets();
InputStream in = assetManager.open("data/file.txt");

OutputStream out = new FileOutputStream(DATA_PATH + "data/file.txt");

// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;

while ((len = in.read(buf)) > 0) {
    out.write(buf, 0, len);
}
in.close();
out.close();
Yuliia Ashomok
  • 8,336
  • 2
  • 60
  • 69
-1

For Move file best way is Renaming it's path with different path and name example:

File from = new File(Environment.getExternalStorage().getAbsolutePath()+"/kaic1/imagem.jpg");
File to = new File(Environment.getExternalStorage().getAbsolutePath()+"/kaic2/imagem.jpg");
from.renameTo(to);
WonderSoftwares
  • 2,800
  • 1
  • 15
  • 21
  • I tried to copy file from the folder «A» to the folder «B», both are stored on `/sdcard/`. The problem is that in folder «B» instead of copying file the code creates an empty folder with the name of the file I want to copy, but there is no the file itself. The original file exists, so what is the problem? I give two absolute paths and perform `from.renameTo(to);`, no exception occurs, but instead of file copy I get an empty folder. There is a needed permission `` in the manifest file. How to fix an issue? – Mike Jun 26 '15 at 18:07
  • 2
    This does not work, because the original question asks to move between internal and external storage, and the docs for .renameTo() explicitly state that this will not work. https://developer.android.com/reference/java/io/File.html#renameTo(java.io.File) – mix3d Aug 01 '16 at 19:02