public class CopyAssets {
public static void copyAssets(Context context) {
AssetManager assetManager = context.getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
if (files != null) for (String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
out = new FileOutputStream(Environment.getExternalStorageDirectory()+"/www/resources/" + filename);
copyFile(in, out);
} catch(IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
}
finally {
if (in != null) {
try {
in.close();
in = null;
} catch (IOException e) {
}
}
if (out != null) {
try {
out.flush();
out.close();
out = null;
} catch (IOException e) {
}
}
}
}
}
public static 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);
}
}}
如您所见,只需在具有活动的java类中创建 CopyAssets 的实例 . 现在这部分很重要,就我在互联网上的测试和研究而言, You cannot use AssetManager if the class has no activity . 它与java类的上下文有关 . 现在, c.copyAssets(getApplicationContext()) 是一种访问该方法的简便方法,其中 c 是 CopyAssets 类的实例 . 根据我的要求,我允许程序将 asset 文件夹中的所有资源文件复制到我的内部目录的 /www/resources/ . 您可以根据自己的使用情况轻松找到需要更改目录的部分 . 如果您需要任何帮助,请随时给我打电话 .
AssetManager am = context.getAssets();
AssetFileDescriptor afd = null;
try {
afd = am.openFd( "MyFile.dat");
// Create new file to copy into.
File file = new File(Environment.getExternalStorageDirectory() + java.io.File.separator + "NewFile.dat");
file.createNewFile();
copyFdToFile(afd.getFileDescriptor(), file);
} catch (IOException e) {
e.printStackTrace();
}
一种复制文件而无需循环的方法 .
public static void copyFdToFile(FileDescriptor 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();
}
}
4
尝试这个更简单,这将有助于你:
// Open your local db as the input stream
InputStream myInput = _context.getAssets().open(YOUR FILE NAME);
// Path to the just created empty db
String outFileName =SDCARD PATH + YOUR FILE NAME;
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
// transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
/**
*
* Info: prior to Android 2.3, any compressed asset file with an
* uncompressed size of over 1 MB cannot be read from the APK. So this
* should only be used if the device has android 2.3 or later running!
*
* @param c
* @param targetFolder
* e.g. {@link Environment#getExternalStorageDirectory()}
* @throws Exception
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public static boolean copyAssets(AssetManager assetManager,
File targetFolder) throws Exception {
Log.i(LOG_TAG, "Copying files from assets to folder " + targetFolder);
return copyAssets(assetManager, "", targetFolder);
}
/**
* The files will be copied at the location targetFolder+path so if you
* enter path="abc" and targetfolder="sdcard" the files will be located in
* "sdcard/abc"
*
* @param assetManager
* @param path
* @param targetFolder
* @return
* @throws Exception
*/
public static boolean copyAssets(AssetManager assetManager, String path,
File targetFolder) throws Exception {
Log.i(LOG_TAG, "Copying " + path + " to " + targetFolder);
String sources[] = assetManager.list(path);
if (sources.length == 0) { // its not a folder, so its a file:
copyAssetFileToFolder(assetManager, path, targetFolder);
} else { // its a folder:
if (path.startsWith("images") || path.startsWith("sounds")
|| path.startsWith("webkit")) {
Log.i(LOG_TAG, " > Skipping " + path);
return false;
}
File targetDir = new File(targetFolder, path);
targetDir.mkdirs();
for (String source : sources) {
String fullSourcePath = path.equals("") ? source : (path
+ File.separator + source);
copyAssets(assetManager, fullSourcePath, targetFolder);
}
}
return true;
}
private static void copyAssetFileToFolder(AssetManager assetManager,
String fullAssetPath, File targetBasePath) throws IOException {
InputStream in = assetManager.open(fullAssetPath);
OutputStream out = new FileOutputStream(new File(targetBasePath,
fullAssetPath));
byte[] buffer = new byte[16 * 1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.flush();
out.close();
}
18 回答
如果其他人遇到同样的问题,我就这样做了
参考:Move file using Java
稍微修改上述答案以递归方式复制文件夹并容纳自定义目标 .
这是迄今为止我能够在互联网上找到的最佳解决方案 . 我使用了以下链接https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217,
但它有一个错误,我修复,然后它就像一个魅力 . 这是我的代码 . 您可以轻松使用它,因为它是一个独立的java类 .
如您所见,只需在具有活动的java类中创建
CopyAssets
的实例 . 现在这部分很重要,就我在互联网上的测试和研究而言,You cannot use AssetManager if the class has no activity
. 它与java类的上下文有关 .现在,
c.copyAssets(getApplicationContext())
是一种访问该方法的简便方法,其中c
是CopyAssets
类的实例 . 根据我的要求,我允许程序将asset
文件夹中的所有资源文件复制到我的内部目录的/www/resources/
.您可以根据自己的使用情况轻松找到需要更改目录的部分 . 如果您需要任何帮助,请随时给我打电话 .
我知道这已经得到了解答,但我有一种更优雅的方式从资产目录复制到SD卡上的文件 . 它不需要“for”循环,而是使用File Streams和Channels来完成工作 .
(注意)如果使用任何类型的压缩文件,APK,PDF,...您可能希望在插入资产之前重命名文件扩展名,然后在将其复制到SD卡后重命名)
一种复制文件而无需循环的方法 .
尝试这个更简单,这将有助于你:
由于某些错误,上述解决方案无效:
目录创建不起作用
Android返回的
资产还包含三个文件夹:图像,声音和webkit
添加了处理大文件的方法:将扩展名.mp3添加到项目的assets文件夹中的文件中,并且在复制期间,目标文件将没有.mp3扩展名
这是代码(我离开了Log语句但你现在可以删除它们):
编辑:纠正错位的“;”这是一个系统的“无法创建目录”的错误 .
将所有文件和目录从资产复制到您的文件夹!
复制更好用apache commons io
//这是复制的主要方法
基本上有两种方法可以做到这一点 .
首先,您可以使用AssetManager.open,并按Rohith Nandakumar所述并迭代输入流 .
其次,您可以使用AssetManager.openFd,它允许您使用FileChannel(其中包含[transferTo]([https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long](https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long),long,java.nio.channels.WritableByteChannel))和[transferFrom]([https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel](https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel),long,long))方法),所以你不必自己循环输入流 .
我将在这里描述openFd方法 .
压缩
首先,您需要确保文件未压缩存储 . 打包系统可以选择使用未标记为noCompress的扩展名压缩任何文件,并且压缩文件不能进行内存映射,因此在这种情况下您必须依赖AssetManager.open .
您可以在文件中添加'.mp3'扩展名以阻止其被压缩,但正确的解决方案是修改您的app / build.gradle文件并添加以下行(以禁用PDF文件的压缩)
文件打包
请注意,打包程序仍然可以将多个文件打包成一个,因此您不能只读取AssetManager为您提供的整个文件 . 您需要向AssetFileDescriptor询问您需要哪些部件 .
查找打包文件的正确部分
一旦确保文件未压缩存储,就可以使用AssetManager.openFd方法获取AssetFileDescriptor,该方法可用于获取包含FileChannel的FileInputStream(与AssetManager.open不同,后者返回一个InputStream) . 它还包含starting offset (getStartOffset)和size (getLength),您需要获取文件的正确部分 .
实施
下面给出了一个示例实现:
这个答案基于JPM's answer .
使用这个问题的答案中的一些概念,我写了一个名为
AssetCopier
的类,使复制/assets/
简单 . 它在github上可用,可以使用jitpack.io访问:有关详细信息,请参阅https://github.com/flipagram/android-assetcopier .
嗨,大家好,我做过这样的事 . 对于要复制的第N个深度复制文件夹和文件 . 允许您复制所有目录结构以从Android AssetManager复制:)
最后创建一个Asynctask:
从你的活动中调用它:
这是当前Android设备的清理版本,功能方法设计如此你可以将它复制到AssetsHelper类,例如;)
@DannyA修改了这个SO的答案
Preparations
在
src/main/assets
添加名称为fold
的文件夹Usage
在外部目录中查找折叠资产中的所有文件和目录
好例子 . 回答了我如何访问assets文件夹中的文件的问题 .
只有改变我建议是在for循环中 . 以下格式也适用,是首选:
根据您的解决方案,我做了一些自己的事情来允许子文件夹 . 有人可能会觉得这很有帮助:
...
...
使用AssetManager,它允许读取资产中的文件 . 然后使用常规Java IO将文件写入sdcard .
谷歌是你的朋友,搜索一个例子 .
改变这些代码的部分:
前面的示例是针对Pdfs的,例如.txt
基于Yoram Cohen的回答,这是一个支持非静态目标目录的版本 .
使用
copyFileOrDir(getDataDir(), "")
将Invoque写入 internal app存储文件夹/ data / data / pkg_name /支持子文件夹 .
支持自定义和非静态目标目录
避免复制“图像”等虚假资产文件夹之类的
基于Rohith Nandakumar的解决方案,我做了一些我自己的事情来从资产的子文件夹中复制文件(即“assets / MyFolder ”) . 此外,我在尝试再次复制之前检查文件是否已存在于SD卡中 .