【问题标题】:Copy directory from Assets to local directory将目录从 Assets 复制到本地目录
【发布时间】:2016-01-19 09:47:49
【问题描述】:

我正在尝试使用资产文件夹中的目录并将其作为File 访问。是否可以以File 的形式访问 Assets 目录中的某些内容?如果没有,如何将 Assets 文件夹中的目录复制到应用程序的本地目录?

我会像这样复制一个文件:

    try
    {
        InputStream stream = this.getAssets().open("myFile");
        OutputStream output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/myNewFile"));

        byte data[] = new byte[1024];
        int count;

        while((count = stream.read(data)) != -1)
        {
            output.write(data, 0, count);
        }

        output.flush();
        output.close();
        stream.close();
    }
    catch(IOException e)
    {
        e.printStackTrace();
    }

但是,我不确定如何为目录执行此操作。

我宁愿不围绕不起作用的东西构建我的基础架构,那么我将如何将目录从 Assets 复制到本地目录,或者是否可以以 File 访问我的 Assets 中的目录?

编辑

这是我为自己的项目解决的方法:

InputStream stream = null;
OutputStream output = null;

for(String fileName : this.getAssets().list("demopass"))
{
    stream = this.getAssets().open("directoryName/" + fileName);
    output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/newDirectory/" + fileName));

    byte data[] = new byte[1024];
    int count;

    while((count = stream.read(data)) != -1)
    {
        output.write(data, 0, count);
    }

    output.flush();
    output.close();
    stream.close();

    stream = null;
    output = null;
}

【问题讨论】:

标签: android


【解决方案1】:

正如 dmaxi 在上面的评论中所建议的,您可以使用他的链接,使用以下代码:

    void displayFiles (AssetManager mgr, String path) {
        try {
            String list[] = mgr.list(path);
            if (list != null)
                for (int i=0; i<list.length; ++i)
                {
                    Log.v("Assets:", path +"/"+ list[i]);
                    displayFiles(mgr, path + "/" + list[i]);
                }
        } catch (IOException e) {
             Log.v("List error:", "can't list" + path);
        }
     }

我接受了this link。 也许你可以把这段代码和前面的代码结合起来。

编辑:另见AssetManager

private void copyFolder(String name) {
            // "Name" is the name of your folder!
    AssetManager assetManager = getAssets();
    String[] files = null;

    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state)) {
        // We can read and write the media
        // Checking file on assets subfolder
        try {
            files = assetManager.list(name);
        } catch (IOException e) {
            Log.e("ERROR", "Failed to get asset file list.", e);
        }
        // Analyzing all file on assets subfolder
        for(String filename : files) {
            InputStream in = null;
            OutputStream out = null;
            // First: checking if there is already a target folder
            File folder = new File(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name);
            boolean success = true;
            if (!folder.exists()) {
                success = folder.mkdir();
            }
            if (success) {
                // Moving all the files on external SD
                try {
                    in = assetManager.open(name + "/" +filename);
                    out = new FileOutputStream(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
                    Log.i("WEBVIEW", Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
                    copyFile(in, out);
                    in.close();
                    in = null;
                    out.flush();
                    out.close();
                    out = null;
                } catch(IOException e) {
                    Log.e("ERROR", "Failed to copy asset file: " + filename, e);
                } finally {
                    // Edit 3 (after MMs comment)
                    in.close();
                    in = null;
                    out.flush();
                    out.close();
                    out = null;
                }
            }
            else {
                // Do something else on failure
            }       
        }
    } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        // We can only read the media
    } else {
        // Something else is wrong. It may be one of many other states, but all we need
        // is to know is we can neither read nor write
    }
}

// Method used by copyAssets() on purpose to copy a file.
private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1) {
        out.write(buffer, 0, read);
    }
}

编辑 2: 我在上面添加了一个示例:这段代码仅将特定文件夹从资产复制到 sd 卡。让我知道它是否有效!

【讨论】:

  • 这对我复制目录没有帮助,因为我只能复制目录中的文件,列出所有文件将复制所有文件。我希望复制整个目录,而不是资产目录中的每个文件。
  • @RileyE 我很好奇找不到解决方案,我已经详细说明了解决方案,看看我的答案!我希望它有效。
  • 我认为这会奏效。我不知道如何让getAssets(String) 中的字符串参数起作用。如果我能让它发挥作用,我会跟进我自己的解决方案。
  • 如果失败,您不会关闭流(使用 finally 块!)
  • Thumbsup ;) 我仍然认为 可能为 null,如果您获得 NPE,您将不会关闭它,但我不确定。
【解决方案2】:

您可以使用以下方法将资产文件夹复制到 SD 卡中的某个位置。从您的调用方法只需调用 moveAssetToStorageDir("") 即可移动整个资产文件夹。如果是子文件夹,您可以指定资产文件夹内的相对路径。

public void moveAssetToStorageDir(String path){
    File file = getExternalFilesDir(null);
    String rootPath = file.getPath() + "/" + path;
    try{
        String [] paths = getAssets().list(path);
        for(int i=0; i<paths.length; i++){
            if(paths[i].indexOf(".")==-1){
                File dir = new File(rootPath + paths[i]);
                dir.mkdir();
                moveAssetToStorageDir(paths[i]);
            }else {
                File dest = null;
                InputStream in = null;
                if(path.length() == 0) {
                    dest = new File(rootPath + paths[i]);
                    in = getAssets().open(paths[i]);
                }else{
                    dest = new File(rootPath + "/" + paths[i]);
                    in = getAssets().open(path + "/" + paths[i]);
                }
                dest.createNewFile();
                FileOutputStream out = new FileOutputStream(dest);
                byte [] buff = new byte[in.available()];
                in.read(buff);
                out.write(buff);
                out.close();
                in.close();
            }
        }
    }catch (Exception exp){
        exp.printStackTrace();
    }
}

【讨论】:

    【解决方案3】:

    从资产中移动任意目录和文件文件夹

    问题是……资产是特殊的。您不能将其包装在 File 对象中并询问 isDirectory(),并且您不能将这些资产传递到 NDK。所以最好将它们包装起来并将它们移动到缓存目录或 SDCard 上,这就是你在这里的原因。

    我已经看到了许多 SO 答案,其中涉及一些版本的滚动文件或目录名称字符串数组,然后创建目录,然后进行递归调用并复制单个文件。这会导致您创建一个文件夹或文件,而您无法从资产中分辨出您拥有的资产。

    将其制作为 Zip 文件

    我的建议是将您想要发送到 SDCard 或内部缓存文件夹的每个任意资产集合都压缩起来。问题的结构更符合资产概念。

    AssetManager assetManager = context.getAssets();
    String fullAssetPath = fromAssetPath + "/" + zipFilename;
    String toPath = "/wherever/I/want";
    
    try {
        InputStream inputStream = assetManager.open(fullAssetPath);
        ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
    
        ZipEntry zipEntry;
        byte[] buffer = new byte[8192];
        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
            String fileOrDirectory = zipEntry.getName();
    
            Uri.Builder builder = new Uri.Builder();
            builder.scheme("file");
            builder.appendPath(toPath);
            builder.appendPath(fileOrDirectory);
            String fullToPath = builder.build().getPath();
    
            if (zipEntry.isDirectory()) {
                File directory = new File(fullToPath);
                directory.mkdirs();
                continue;
            }   
    
            FileOutputStream fileOutputStream = new FileOutputStream(fullToPath);
            while ((count = zipInputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, count);
            }
            fileOutputStream.close();
            zipInputStream.closeEntry();
        }
    
        zipInputStream.close();
    
    } catch (IOException e) {
        Log.e(TAG, e.getLocalizedMessage());
    }
    

    关于缓冲区大小的小记

    我见过很多涉及非常小的缓冲区大小的示例,例如 1024。除非您只是想浪费时间,否则请随意尝试更大的字节缓冲区大小。即使是我选择的 8192 在现代硬件上也可能很小。

    避免字符串路径

    注意使用Uri.Builder 来构造路径。比起directory + "/" + file,我更喜欢这种路径构建方式。然后你在做生意,为了保持一致性,避免分配String d = "myDirectory/"String f = "/file.txt" 和其他类似的字符串黑客废话。

    【讨论】:

    • 谢谢,我正在寻找一个模型来执行这样的功能。
    • @Cameron Lowell Palmer 使用 Executors.newSingleThreadExecutor() 将您的示例放入 AsyncTask 或 ExecutorService 有什么缺点吗?
    【解决方案4】:

    这是 OP 答案的干净版本。

    public void copyAssetFolderToFolder(Context activity, String assetsFolder, File destinationFolder) {
      InputStream stream = null;
      OutputStream output = null;
      try {
        for (String fileName : activity.getAssets().list(assetsFolder)) {
          stream = activity.getAssets().open(assetsFolder + ((assetsFolder.endsWith(File.pathSeparator))?"":File.pathSeparator) + fileName);
          output = new BufferedOutputStream(new FileOutputStream(new File(destinationFolder, fileName)));
    
          byte data[] = new byte[1024];
          int count;
    
          while ((count = stream.read(data)) != -1) {
            output.write(data, 0, count);
          }
    
          output.flush();
          output.close();
          stream.close();
    
          stream = null;
          output = null;
        }
      } catch (/*any*/Exception e){e.printStackTrace();}
    }
    

    为了将来参考,请为大家省去麻烦并发布上下文完整的源列表。对于初学者和专家来说,这个网站可能是一个很好的编码资源,只要你能发布完整的答案。不能假设其他任何人“理解”随机代码块所属的位置,或者代码应该在其中执行的上下文。

    此示例调用活动的上下文,其中包含 getAssets() 方法。在 android 平台中,它们是除 Activity 之外的其他类,它们可以提供此上下文。一个例子是(通用参考)Service 类。

    【讨论】:

    • “完成”表示在方法/类头中;并且没有通用的椭圆代码概括,例如:... 做一些事情...。初学者不会知道伪代码和实际代码之间的区别。
    • 你需要修复 + ((assetsFolder.endsWith(File.pathSeparator))?"":File.pathSeparator) + 结果是:而不是 / 或 \ 我把它改成 + "/" + 并且代码完美运行,请更新我大约需要 1 小时搜索的代码
    【解决方案5】:

    这是复制资产文件夹的代码,其中目录和文件都复制到 sdcard 文件夹中... 这一款非常适合我...

    public void copyFileOrDir(String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path);
        } else {
            String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
            File dir = new File(fullPath);
            if (!dir.exists())
                dir.mkdir();
            for (int i = 0; i < assets.length; ++i) {
                copyFileOrDir(path + "/" + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
    }
    
    private void copyFile(String filename) {
    AssetManager assetManager = this.getAssets();
    
    InputStream in = null;
    OutputStream out = null;
    try {
        in = assetManager.open(filename);
        String newFileName = "/data/data/" + this.getPackageName() + "/" + filename;
        out = new FileOutputStream(newFileName);
    
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", e.getMessage());
    }
    
    }
    

    【讨论】:

      【解决方案6】:

      这是执行此操作的递归函数 - copyAssetFolder

      public static boolean copyAssetFolder(Context context, String srcName, String dstName) {
          try {
              boolean result = true;
              String fileList[] = context.getAssets().list(srcName);
              if (fileList == null) return false;
      
              if (fileList.length == 0) {
                  result = copyAssetFile(context, srcName, dstName);
              } else {
                  File file = new File(dstName);
                  result = file.mkdirs();
                  for (String filename : fileList) {
                      result &= copyAssetFolder(context, srcName + File.separator + filename, dstName + File.separator + filename);
                  }
              }
              return result;
          } catch (IOException e) {
              e.printStackTrace();
              return false;
          }
      }
      
      public static boolean copyAssetFile(Context context, String srcName, String dstName) {
          try {
              InputStream in = context.getAssets().open(srcName);
              File outFile = new File(dstName);
              OutputStream out = new FileOutputStream(outFile);
              byte[] buffer = new byte[1024];
              int read;
              while ((read = in.read(buffer)) != -1) {
                  out.write(buffer, 0, read);
              }
              in.close();
              out.close();
              return true;
          } catch (IOException e) {
              e.printStackTrace();
              return false;
          }
      }
      

      Kotlin

      中也一样
      fun AssetManager.copyAssetFolder(srcName: String, dstName: String): Boolean {
          return try {
              var result = true
              val fileList = this.list(srcName) ?: return false
              if (fileList.isEmpty()) {
                  result = copyAssetFile(srcName, dstName)
              } else {
                  val file = File(dstName)
                  result = file.mkdirs()
                  for (filename in fileList) {
                      result = result and copyAssetFolder(
                          srcName + separator.toString() + filename,
                          dstName + separator.toString() + filename
                      )
                  }
              }
              result
          } catch (e: IOException) {
              e.printStackTrace()
              false
          }
      }
      
      fun AssetManager.copyAssetFile(srcName: String, dstName: String): Boolean {
          return try {
              val inStream = this.open(srcName)
              val outFile = File(dstName)
              val out: OutputStream = FileOutputStream(outFile)
              val buffer = ByteArray(1024)
              var read: Int
              while (inStream.read(buffer).also { read = it } != -1) {
                  out.write(buffer, 0, read)
              }
              inStream.close()
              out.close()
              true
          } catch (e: IOException) {
              e.printStackTrace()
              false
          }
      }
      

      【讨论】:

        【解决方案7】:

        这是一个用 kotlin 编写的递归解决方案。它适用于文件和目录。

        用法 - copyAssetDir(context, "&lt;asset path&gt;", "&lt;dest dir&gt;")

        import android.content.Context
        import java.io.File
        import java.io.FileOutputStream
        
        fun copyAssetDir(context: Context, assetPath: String, destDirPath: String) {
            walkAssetDir(context, assetPath) {
                copyAssetFile(context, it, "$destDirPath/$it")
            }
        }
        
        fun walkAssetDir(context: Context, assetPath: String, callback: ((String) -> Unit)) {
            val children = context.assets.list(assetPath) ?: return
            if (children.isEmpty()) {
                callback(assetPath)
            } else {
                for (child in children) {
                    walkAssetDir(context, "$assetPath/$child", callback)
                }
            }
        }
        
        fun copyAssetFile(context: Context, assetPath: String, destPath: String): File {
            val destFile = File(destPath)
            File(destFile.parent).mkdirs()
            destFile.createNewFile()
        
            context.assets.open(assetPath).use { src ->
                FileOutputStream(destFile).use { dest ->
                    src.copyTo(dest)
                }
            }
        
            return destFile
        }
        

        【讨论】:

          猜你喜欢
          • 2011-10-14
          • 2012-01-22
          • 1970-01-01
          • 2017-01-16
          • 2011-11-03
          • 2021-06-06
          • 2016-08-05
          • 2014-11-07
          • 2020-06-02
          相关资源
          最近更新 更多