23

我正在尝试使用资产文件夹中的目录并将其作为File. 是否可以访问 Assets 目录中的某些内容File?如果没有,如何将目录从 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 复制到本地目录,或者是否可以将我的 Assets 中的目录作为File?

编辑

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

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

7 回答 7

11

正如 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);
        }
     }

我把它放在这个链接上。也许您可以将此代码与先例结合起来。

编辑:另见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 卡。让我知道它是否有效!

于 2013-03-22T16:51:48.120 回答
4

这是执行此操作的递归函数 - 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
    }
}
于 2019-03-15T13:38:52.503 回答
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();
    }
}
于 2016-01-07T06:09:40.677 回答
2

这是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类。

于 2017-11-06T15:41:22.753 回答
1

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

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

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

将其设为 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 在现代硬件上也可能很小。

避免 Stringy 路径

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

于 2017-02-23T12:27:16.190 回答
0

这是复制资产文件夹的代码,其中目录和文件都复制到 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());
}

}
于 2018-01-02T09:34:36.837 回答
0

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

用法 -copyAssetDir(context, "<asset path>", "<dest dir>")

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
}
于 2020-04-08T11:43:51.653 回答