【问题标题】:How to move/rename file from internal app storage to external storage on Android?如何在 Android 上将文件从内部应用存储移动/重命名到外部存储?
【发布时间】:2011-01-22 19:18:39
【问题描述】:

我正在从 Internet 下载文件并将流数据保存到我的应用程序内部存储中的一个临时文件,该文件由 getFilesDir() 提供。

下载完成后,我需要将临时文件移动到外部存储器(通常是 SD 卡)上的下载目录中。但出于某种原因, File.renameTo() 不适用于此。我猜是有问题,因为它是两个独立的文件系统,但我仍然可以直接下载到 SD 卡并且文件 URI 是正确的。

是否有另一种简单快捷的方法将该文件从内部存储器传输到外部存储器,还是我必须进行字节流复制并删除原始文件?

【问题讨论】:

  • 老问题,但要回答为什么 file.renameTo() 不起作用,这是因为该函数要求位置位于同一挂载点上,而 sdcard 和 internal 是不同的位置: “两条路径都在同一个挂载点上。在 Android 上,应用程序在尝试在内部存储和 SD 卡之间进行复制时最有可能遇到此限制。” developer.android.com/reference/java/io/…
  • 2020,File file = new File(getExternalFilesDir(null), fileName + ".mp3");已经给了内部存储

标签: java android file


【解决方案1】:

使用以下代码将文件从内存复制到 SD 卡,反之亦然:

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();
    }
}

而且 - 它有效...

【讨论】:

  • 如果写/读外部存储器,请确保你有以下权限
  • 构造函数不应该在try-block里面吗?
  • 您不必关闭来自new FileInputStream(src)new FileOutputStream(dst) 的结果,它们甚至在这里都没有引用吗?
  • 不,关闭频道会关闭这些流
  • @barmaley:文件夹呢?而不是文件。我有 3 个文件夹,我想将其剪切粘贴到新位置。但它在FileChannel inChannel = new FileInputStream(src).getChannel(); 行显示一个错误,说 filenotfound 异常。虽然文件存在。 :( 有什么解决办法吗?
【解决方案2】:

内部和外部存储器是两个不同的文件系统。因此 renameTo() 失败。

您必须复制文件并删除原始文件

【讨论】:

    【解决方案3】:

    复制文件后(如@barmaley 的出色答案所示),不要忘记将其公开到设备的图库中,以便用户稍后查看。

    必须手动完成的原因是

    Android 仅在重新启动和(重新)安装时运行完整的媒体扫描 SD卡

    (如this guide 所示)。

    更简单的方法是发送一个广播以调用扫描:

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

    瞧!您现在可以在设备的图库中查看您的文件。

    【讨论】:

    • 一直在寻找这个解决方案! +1
    【解决方案4】:

    使用您自己的函数进行复制的替代方法是在名为 copyFile 的函数中使用 Apache 库的类 "FileUtils" :

    FileUtils.copyFile(src, dst, true);
    

    【讨论】:

    • 这对于较大的文件是必要的(超过使用 Android 的 FileChanel)
    • 但它只适用于Android Q及以上
    • @AungMyoLwin 是什么?
    【解决方案5】:

    对@barmaley 的代码做了一些细微的修改

    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;
        }
    

    【讨论】:

    • 您对这些更改有任何理由或评论吗?为什么这是一项改进?
    • 旧答案,但我必须注意,这对于修改来说更糟。更糟糕的是,它主动隐藏了错误条件。看起来很方便,但实用函数无法知道调用者是否需要区分 FileNotFound、NonWritableChannel 等。这使得下游逻辑必然模糊。
    【解决方案6】:

    图片:

    • 这是内部路径:pathInternal
    • 这是外部路径: 路径外部

    试试这个代码:

    public void moveIn (String pathInternal, String pathExternal) {
        File fInternal = new File (pathInternal);
        File fExternal = new File (pathExternal);
        if (fInternal.exists()) {
            fInternal.renameTo(fExternal);
        }
    }
    

    【讨论】:

    • 这不起作用,因为“两条路径都在同一个挂载点上。在 Android 上,应用程序在尝试在内部存储和 SD 卡之间复制时最有可能遇到此限制。” developer.android.com/reference/java/io/…
    【解决方案7】:

    您可以使用 byte[]

    的操作来做到这一点

    在你的类中定义:

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

    然后:

    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();
    

    【讨论】:

      【解决方案8】:

      对于移动文件,最好的方法是用不同的路径和名称重命名它的路径 示例:

      File from = new File(Environment.getExternalStorage().getAbsolutePath()+"/kaic1/imagem.jpg");
      File to = new File(Environment.getExternalStorage().getAbsolutePath()+"/kaic2/imagem.jpg");
      from.renameTo(to);
      

      【讨论】:

      • 我试图将文件从文件夹«A»复制到文件夹«B»,两者都存储在/sdcard/。问题是在文件夹«B»中,代码没有复制文件,而是使用我要复制的文件的名称创建了一个空文件夹,但没有文件本身。原文件存在,请问有什么问题?我给出了两个绝对路径并执行from.renameTo(to);,没有发生异常,但是我得到一个空文件夹而不是文件副本。清单文件中有必要的权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>。如何解决问题?
      • 这不起作用,因为原始问题要求在内部和外部存储之间移动,并且 .renameTo() 的文档明确指出这不起作用。 developer.android.com/reference/java/io/…
      猜你喜欢
      • 2015-09-14
      • 2023-03-05
      • 1970-01-01
      • 2015-03-11
      • 1970-01-01
      • 2014-06-12
      • 2020-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多