如何将文件从“资产”文件夹复制到 SD 卡?
Posted
技术标签:
【中文标题】如何将文件从“资产”文件夹复制到 SD 卡?【英文标题】:How to copy files from 'assets' folder to sdcard? 【发布时间】:2011-05-25 18:02:21 【问题描述】:assets
文件夹中有一些文件。我需要将它们全部复制到一个文件夹中,例如 /sdcard/folder。我想从一个线程中做到这一点。我该怎么做?
【问题讨论】:
你在找这个***.com/questions/4447477/… 在您复制/粘贴以下(很棒的!)解决方案之一之前,请考虑使用此库在一行代码中完成:***.com/a/41970539/9648 【参考方案1】:如果其他人有同样的问题,我就是这样做的
private void copyAssets()
AssetManager assetManager = 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);
File outFile = new File(getExternalFilesDir(null), filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
catch(IOException e)
Log.e("tag", "Failed to copy asset file: " + filename, e);
finally
if (in != null)
try
in.close();
catch (IOException e)
// NOOP
if (out != null)
try
out.close();
catch (IOException e)
// NOOP
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);
参考:Move file using Java
【讨论】:
要在 sdcard 中写入文件,您必须授予清单上的权限,例如Failed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)
对我而言,此代码仅在我添加此代码时才有效:in = assetManager.open("images-wall/"+filename);
其中“images-wall”是我在资产中的文件夹【参考方案2】:
根据您的解决方案,我自己做了一些事情来允许子文件夹。有人可能会觉得这很有帮助:
...
copyFileOrDir("myrootdir");
...
private 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());
【讨论】:
assetManager.list(path)
在设备上可能很慢,要预先创建资产路径列表,可以从assets
dir: find . -name "*" -type f -exec ls -l \; | awk 'print substr($9,3)' >> assets.list
使用这个 sn-p
不错的解决方案!唯一需要的修复是在 copyFileOrDir() 开头修剪前导分隔符: path= path.startsWith("/") ? path.substring(1) : 路径;
这个***在某些设备上,例如:S5
用 this.getFilesDir().getAbsolutePath() 替换 "/data/data/" + this.getPackageName()
...并关闭finally
块中的流))【参考方案3】:
由于一些错误,上述解决方案不起作用:
目录创建失败 Android 返回的资产还包含三个文件夹:images、sounds 和 webkit 添加了处理大文件的方法:将扩展名 .mp3 添加到项目中 assets 文件夹中的文件中,并且在复制过程中目标文件将没有 .mp3 扩展名这是代码(我留下了 Log 语句,但您现在可以删除它们):
final static String TARGET_BASE_PATH = "/sdcard/appname/voices/";
private void copyFilesToSdCard()
copyFileOrDir(""); // copy all files in assets folder in my project
private void copyFileOrDir(String path)
AssetManager assetManager = this.getAssets();
String assets[] = null;
try
Log.i("tag", "copyFileOrDir() "+path);
assets = assetManager.list(path);
if (assets.length == 0)
copyFile(path);
else
String fullPath = TARGET_BASE_PATH + path;
Log.i("tag", "path="+fullPath);
File dir = new File(fullPath);
if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
if (!dir.mkdirs())
Log.i("tag", "could not create dir "+fullPath);
for (int i = 0; i < assets.length; ++i)
String p;
if (path.equals(""))
p = "";
else
p = path + "/";
if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
copyFileOrDir( p + 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;
String newFileName = null;
try
Log.i("tag", "copyFile() "+filename);
in = assetManager.open(filename);
if (filename.endsWith(".jpg")) // extension was added to avoid compression on APK file
newFileName = TARGET_BASE_PATH + filename.substring(0, filename.length()-4);
else
newFileName = TARGET_BASE_PATH + 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", "Exception in copyFile() of "+newFileName);
Log.e("tag", "Exception in copyFile() "+e.toString());
编辑:更正了一个放错位置的“;”这引发了系统性的“无法创建目录”错误。
【讨论】:
这必须成为解决方案! 注意:Log.i("tag", "could not create dir "+fullPath);总是发生为 ;在 if 上放错了位置。 太棒了!非常感谢!但是为什么要检查 jpg 文件呢?【参考方案4】:我知道这个问题已经得到解答,但我有一种更优雅的方式可以从资产目录复制到 sdcard 上的文件。它不需要“for”循环,而是使用文件流和通道来完成工作。
(注意)如果使用任何类型的压缩文件,APK,PDF,...您可能需要在插入资产之前重命名文件扩展名,然后在将其复制到 SD 卡后重命名)
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();
【讨论】:
比其他解决方案更喜欢这个,有点整洁。对我的轻微修改,包括创建丢失的文件文件夹。干杯! 这对我来说无法通过文件描述符This file can not be opened as a file descriptor; it is probably compressed
——它是一个pdf文件。知道如何解决吗?
这假定 inChannel.size() 返回文件大小的大小。它使no such guarantee。我得到 2.5 MiB 的 2 个文件,每个文件 450 KiB。
我刚刚发现 AssetFileDescriptor.getLength() 会返回正确的文件大小。
除上述之外,资产可能不在文件描述符中的位置 0 处开始。 AssetFileDescriptor.getStartOffset() 将返回起始偏移量。【参考方案5】:
这将是 Kotlin 中的简洁方式。
fun AssetManager.copyRecursively(assetPath: String, targetFile: File)
val list = list(assetPath)
if (list.isEmpty()) // assetPath is file
open(assetPath).use input ->
FileOutputStream(targetFile.absolutePath).use output ->
input.copyTo(output)
output.flush()
else // assetPath is folder
targetFile.delete()
targetFile.mkdir()
list.forEach
copyRecursively("$assetPath/$it", File(targetFile, it))
【讨论】:
list(assetPath)?.let ... ,实际上。它是可空的。【参考方案6】:试试这个,它更简单,这将帮助你:
// 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();
【讨论】:
【参考方案7】:这是针对当前 Android 设备的清理版本,功能方法设计,以便您可以将其复制到 AssetsHelper 类中,例如 ;)
/**
*
* 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();
【讨论】:
【参考方案8】:@DannyA 修改了这个SO 答案
private void copyAssets(String path, String outPath)
AssetManager assetManager = this.getAssets();
String assets[];
try
assets = assetManager.list(path);
if (assets.length == 0)
copyFile(path, outPath);
else
String fullPath = outPath + "/" + path;
File dir = new File(fullPath);
if (!dir.exists())
if (!dir.mkdir()) Log.e(TAG, "No create external directory: " + dir );
for (String asset : assets)
copyAssets(path + "/" + asset, outPath);
catch (IOException ex)
Log.e(TAG, "I/O Exception", ex);
private void copyFile(String filename, String outPath)
AssetManager assetManager = this.getAssets();
InputStream in;
OutputStream out;
try
in = assetManager.open(filename);
String newFileName = outPath + "/" + 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();
out.flush();
out.close();
catch (Exception e)
Log.e(TAG, e.getMessage());
准备工作
在src/main/assets
添加名称为fold
的文件夹
用法
File outDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
copyAssets("fold",outDir.toString());
在外部目录中找到折叠资产中的所有文件和目录
【讨论】:
【参考方案9】:使用这个问题的答案中的一些概念,我编写了一个名为AssetCopier
的类,以使复制/assets/
变得简单。它在github 上可用,可以通过jitpack.io 访问:
new AssetCopier(MainActivity.this)
.withFileScanning()
.copy("tocopy", destDir);
更多详情请见https://github.com/flipagram/android-assetcopier。
【讨论】:
【参考方案10】:将所有文件和目录从资产复制到您的文件夹!
为了更好地复制使用 apache commons io
public void doCopyAssets() throws IOException
File externalFilesDir = context.getExternalFilesDir(null);
doCopy("", externalFilesDir.getPath());
//这是复制的主要方法
private void doCopy(String dirName, String outPath) throws IOException
String[] srcFiles = assets.list(dirName);//for directory
for (String srcFileName : srcFiles)
String outFileName = outPath + File.separator + srcFileName;
String inFileName = dirName + File.separator + srcFileName;
if (dirName.equals("")) // for first time
inFileName = srcFileName;
try
InputStream inputStream = assets.open(inFileName);
copyAndClose(inputStream, new FileOutputStream(outFileName));
catch (IOException e) //if directory fails exception
new File(outFileName).mkdir();
doCopy(inFileName, outFileName);
public static void closeQuietly(AutoCloseable autoCloseable)
try
if(autoCloseable != null)
autoCloseable.close();
catch(IOException ioe)
//skip
public static void copyAndClose(InputStream input, OutputStream output) throws IOException
copy(input, output);
closeQuietly(input);
closeQuietly(output);
public static void copy(InputStream input, OutputStream output) throws IOException
byte[] buffer = new byte[1024];
int n = 0;
while(-1 != (n = input.read(buffer)))
output.write(buffer, 0, n);
【讨论】:
【参考方案11】:基于 Rohith Nandakumar 的解决方案,我自己做了一些事情来从资产的子文件夹(即“assets/MyFolder”)复制文件。另外,在尝试再次复制之前,我正在检查文件是否已存在于 sdcard 中。
private void copyAssets()
AssetManager assetManager = getAssets();
String[] files = null;
try
files = assetManager.list("MyFolder");
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("MyFolder/"+filename);
File outFile = new File(getExternalFilesDir(null), filename);
if (!(outFile.exists())) // File does not exist...
out = new FileOutputStream(outFile);
copyFile(in, out);
catch(IOException e)
Log.e("tag", "Failed to copy asset file: " + filename, e);
finally
if (in != null)
try
in.close();
catch (IOException e)
// NOOP
if (out != null)
try
out.close();
catch (IOException e)
// NOOP
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);
【讨论】:
【参考方案12】:根据 Yoram Cohen 的回答,这是一个支持非静态目标目录的版本。
使用copyFileOrDir(getDataDir(), "")
调用以写入内部应用存储文件夹/data/data/pkg_name/
避免复制“图像”等假资产文件夹,如
private void copyFileOrDir(String TARGET_BASE_PATH, String path)
AssetManager assetManager = this.getAssets();
String assets[] = null;
try
Log.i("tag", "copyFileOrDir() "+path);
assets = assetManager.list(path);
if (assets.length == 0)
copyFile(TARGET_BASE_PATH, path);
else
String fullPath = TARGET_BASE_PATH + "/" + path;
Log.i("tag", "path="+fullPath);
File dir = new File(fullPath);
if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
if (!dir.mkdirs())
Log.i("tag", "could not create dir "+fullPath);
for (int i = 0; i < assets.length; ++i)
String p;
if (path.equals(""))
p = "";
else
p = path + "/";
if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
copyFileOrDir(TARGET_BASE_PATH, p + assets[i]);
catch (IOException ex)
Log.e("tag", "I/O Exception", ex);
private void copyFile(String TARGET_BASE_PATH, String filename)
AssetManager assetManager = this.getAssets();
InputStream in = null;
OutputStream out = null;
String newFileName = null;
try
Log.i("tag", "copyFile() "+filename);
in = assetManager.open(filename);
if (filename.endsWith(".jpg")) // extension was added to avoid compression on APK file
newFileName = TARGET_BASE_PATH + "/" + filename.substring(0, filename.length()-4);
else
newFileName = TARGET_BASE_PATH + "/" + 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", "Exception in copyFile() of "+newFileName);
Log.e("tag", "Exception in copyFile() "+e.toString());
【讨论】:
【参考方案13】:基本上有两种方法可以做到这一点。
首先,您可以使用AssetManager.open 和,如Rohith Nandakumar 所述,并遍历输入流。
其次,你可以使用AssetManager.openFd,它允许你使用FileChannel(它有[transferTo](https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long, long, java.nio.channels.WritableByteChannel))和[transferFrom](@ 987654326@, long, long)) 方法),因此您不必自己循环输入流。
我将在这里描述openFd方法。
压缩
首先,您需要确保文件未压缩存储。打包系统可能会选择压缩任何扩展名没有标记为 noCompress 的文件,并且压缩后的文件无法进行内存映射,因此您将不得不依赖 AssetManager.open 在那种情况下。
您可以在文件中添加“.mp3”扩展名以阻止其被压缩,但正确的解决方案是修改您的 app/build.gradle 文件并添加以下行(到禁用 PDF 文件压缩)
aaptOptions
noCompress 'pdf'
文件打包
请注意,打包程序仍然可以将多个文件打包成一个,因此您不能只读取 AssetManager 提供给您的整个文件。您需要询问 AssetFileDescriptor 您需要哪些部分。
找到打包文件的正确部分
一旦您确保文件未压缩存储,您可以使用 AssetManager.openFd 方法获取 AssetFileDescriptor,该方法可用于获取 FileInputStream(与返回 InputStream 的 AssetManager.open 不同)包含 FileChannel。它还包含starting offset (getStartOffset) 和size (getLength),您需要获取文件的正确部分。
实施
下面给出一个示例实现:
private void copyFileFromAssets(String in_filename, File out_file)
Log.d("copyFileFromAssets", "Copying file '"+in_filename+"' to '"+out_file.toString()+"'");
AssetManager assetManager = getApplicationContext().getAssets();
FileChannel in_chan = null, out_chan = null;
try
AssetFileDescriptor in_afd = assetManager.openFd(in_filename);
FileInputStream in_stream = in_afd.createInputStream();
in_chan = in_stream.getChannel();
Log.d("copyFileFromAssets", "Asset space in file: start = "+in_afd.getStartOffset()+", length = "+in_afd.getLength());
FileOutputStream out_stream = new FileOutputStream(out_file);
out_chan = out_stream.getChannel();
in_chan.transferTo(in_afd.getStartOffset(), in_afd.getLength(), out_chan);
catch (IOException ioe)
Log.w("copyFileFromAssets", "Failed to copy file '"+in_filename+"' to external storage:"+ioe.toString());
finally
try
if (in_chan != null)
in_chan.close();
if (out_chan != null)
out_chan.close();
catch (IOException ioe)
此答案基于JPM's answer。
【讨论】:
【参考方案14】:您可以使用 Kotlin 分几步完成,这里我只是将几个文件而不是所有文件从 asstes 复制到我的应用程序文件目录。
private fun copyRelatedAssets()
val assets = arrayOf("myhome.html", "support.css", "myscript.js", "style.css")
assets.forEach
val inputStream = requireContext().assets.open(it)
val nameSplit = it.split(".")
val name = nameSplit[0]
val extension = nameSplit[1]
val path = inputStream.getFilePath(requireContext().filesDir, name, extension)
Log.v(TAG, path)
这里是扩展功能,
fun InputStream.getFilePath(dir: File, name: String, extension: String): String
val file = File(dir, "$name.$extension")
val outputStream = FileOutputStream(file)
this.copyTo(outputStream, 4096)
return file.absolutePath
LOGCAT
/data/user/0/com.***.***/files/myhome.html
/data/user/0/com.***.***/files/support.css
/data/user/0/com.***.***/files/myscript.js
/data/user/0/com.***.***/files/style.css
【讨论】:
【参考方案15】:import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MainActivity extends Activity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
copyReadAssets();
private void copyReadAssets()
AssetManager assetManager = getAssets();
InputStream in = null;
OutputStream out = null;
String strDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+ File.separator + "Pdfs";
File fileDir = new File(strDir);
fileDir.mkdirs(); // crear la ruta si no existe
File file = new File(fileDir, "example2.pdf");
try
in = assetManager.open("example.pdf"); //leer el archivo de assets
out = new BufferedOutputStream(new FileOutputStream(file)); //crear el archivo
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
catch (Exception e)
Log.e("tag", e.getMessage());
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + File.separator + "Pdfs" + "/example2.pdf"), "application/pdf");
startActivity(intent);
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);
像这样更改部分代码:
out = new BufferedOutputStream(new FileOutputStream(file));
前面的例子是针对Pdfs的,如果是例子.txt
FileOutputStream fos = new FileOutputStream(file);
【讨论】:
【参考方案16】:嗨,伙计们,我做了这样的事情。 用于第 N 深度复制文件夹和要复制的文件。 这允许您复制所有目录结构以从 Android AssetManager 复制:)
private void manageAssetFolderToSDcard()
try
String arg_assetDir = getApplicationContext().getPackageName();
String arg_destinationDir = FRConstants.ANDROID_DATA + arg_assetDir;
File FolderInCache = new File(arg_destinationDir);
if (!FolderInCache.exists())
copyDirorfileFromAssetManager(arg_assetDir, arg_destinationDir);
catch (IOException e1)
e1.printStackTrace();
public String copyDirorfileFromAssetManager(String arg_assetDir, String arg_destinationDir) throws IOException
File sd_path = Environment.getExternalStorageDirectory();
String dest_dir_path = sd_path + addLeadingSlash(arg_destinationDir);
File dest_dir = new File(dest_dir_path);
createDir(dest_dir);
AssetManager asset_manager = getApplicationContext().getAssets();
String[] files = asset_manager.list(arg_assetDir);
for (int i = 0; i < files.length; i++)
String abs_asset_file_path = addTrailingSlash(arg_assetDir) + files[i];
String sub_files[] = asset_manager.list(abs_asset_file_path);
if (sub_files.length == 0)
// It is a file
String dest_file_path = addTrailingSlash(dest_dir_path) + files[i];
copyAssetFile(abs_asset_file_path, dest_file_path);
else
// It is a sub directory
copyDirorfileFromAssetManager(abs_asset_file_path, addTrailingSlash(arg_destinationDir) + files[i]);
return dest_dir_path;
public void copyAssetFile(String assetFilePath, String destinationFilePath) throws IOException
InputStream in = getApplicationContext().getAssets().open(assetFilePath);
OutputStream out = new FileOutputStream(destinationFilePath);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
out.write(buf, 0, len);
in.close();
out.close();
public String addTrailingSlash(String path)
if (path.charAt(path.length() - 1) != '/')
path += "/";
return path;
public String addLeadingSlash(String path)
if (path.charAt(0) != '/')
path = "/" + path;
return path;
public void createDir(File dir) throws IOException
if (dir.exists())
if (!dir.isDirectory())
throw new IOException("Can't create directory, a file is in the way");
else
dir.mkdirs();
if (!dir.isDirectory())
throw new IOException("Unable to create directory");
最后创建一个Asynctask:
private class ManageAssetFolders extends AsyncTask<Void, Void, Void>
@Override
protected Void doInBackground(Void... arg0)
manageAssetFolderToSDcard();
return null;
从你的活动中调用它:
new ManageAssetFolders().execute();
【讨论】:
【参考方案17】:对上述答案稍作修改,以递归方式复制文件夹并适应自定义目标。
public void copyFileOrDir(String path, String destinationDir)
AssetManager assetManager = this.getAssets();
String assets[] = null;
try
assets = assetManager.list(path);
if (assets.length == 0)
copyFile(path,destinationDir);
else
String fullPath = destinationDir + "/" + path;
File dir = new File(fullPath);
if (!dir.exists())
dir.mkdir();
for (int i = 0; i < assets.length; ++i)
copyFileOrDir(path + "/" + assets[i], destinationDir + path + "/" + assets[i]);
catch (IOException ex)
Log.e("tag", "I/O Exception", ex);
private void copyFile(String filename, String destinationDir)
AssetManager assetManager = this.getAssets();
String newFileName = destinationDir + "/" + filename;
InputStream in = null;
OutputStream out = null;
try
in = assetManager.open(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());
new File(newFileName).setExecutable(true, false);
【讨论】:
【参考方案18】:对于那些正在更新到 Kotlin 的人:
按照this 的步骤避免FileUriExposedExceptions
,
假设用户已授予WRITE_EXTERNAL_STORAGE
权限,并且您的文件位于assets/pdfs/mypdf.pdf
。
private fun openFile()
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try
val file = File("$activity.getExternalFilesDir(null)/$PDF_FILE_NAME")
if (!file.exists())
inputStream = activity.assets.open("$PDF_ASSETS_PATH/$PDF_FILE_NAME")
outputStream = FileOutputStream(file)
copyFile(inputStream, outputStream)
val uri = FileProvider.getUriForFile(
activity,
"$BuildConfig.APPLICATION_ID.provider.GenericFileProvider",
file
)
val intent = Intent(Intent.ACTION_VIEW).apply
setDataAndType(uri, "application/pdf")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
activity.startActivity(intent)
catch (ex: IOException)
ex.printStackTrace()
catch (ex: ActivityNotFoundException)
ex.printStackTrace()
finally
inputStream?.close()
outputStream?.flush()
outputStream?.close()
@Throws(IOException::class)
private fun copyFile(input: InputStream, output: OutputStream)
val buffer = ByteArray(1024)
var read: Int = input.read(buffer)
while (read != -1)
output.write(buffer, 0, read)
read = input.read(buffer)
companion object
private const val PDF_ASSETS_PATH = "pdfs"
private const val PDF_FILE_NAME = "mypdf.pdf"
【讨论】:
【参考方案19】:这是我的个性化文本提取器类,希望对你有用。
package lorenzo.morelli.platedetector;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import com.googlecode.tesseract.android.TessBaseAPI;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class TextExtractor
private final Context context;
private final String dirName;
private final String language;
public TextExtractor(final Context context, final String dirName, final String language)
this.context = context;
this.dirName = dirName;
this.language = language;
public String extractText(final Bitmap bitmap)
final TessBaseAPI tessBaseApi = new TessBaseAPI();
final String datapath = this.context.getFilesDir()+ "/tesseract/";
checkFile(new File(datapath + this.dirName + "/"), datapath, this.dirName, this.language);
tessBaseApi.init(datapath, this.language);
tessBaseApi.setImage(bitmap);
final String extractedText = tessBaseApi.getUTF8Text();
tessBaseApi.end();
return extractedText;
private void checkFile(final File dir, final String datapath, final String dirName, final String language)
//directory does not exist, but we can successfully create it
if (!dir.exists()&& dir.mkdirs())
copyFiles(datapath, dirName, language);
//The directory exists, but there is no data file in it
if(dir.exists())
final String datafilepath = datapath + "/" + dirName + "/" + language + ".traineddata";
final File datafile = new File(datafilepath);
if (!datafile.exists())
copyFiles(datapath, dirName, language);
private void copyFiles(final String datapath, final String dirName, final String language)
try
//location we want the file to be at
final String filepath = datapath + "/" + dirName + "/" + language + ".traineddata";
//get access to AssetManager
final AssetManager assetManager = this.context.getAssets();
//open byte streams for reading/writing
final InputStream instream = assetManager.open(dirName + "/" + language + ".traineddata");
final OutputStream outstream = new FileOutputStream(filepath);
//copy the file to the location specified by filepath
byte[] buffer = new byte[1024];
int read;
while ((read = instream.read(buffer)) != -1)
outstream.write(buffer, 0, read);
outstream.flush();
outstream.close();
instream.close();
catch (IOException e)
e.printStackTrace();
要使用它,您需要经过训练的数据文件。你可以从这个link下载trainddata文件。
下载所需的训练数据文件后,您需要在您的 android 项目中创建一个名为 assets 的 Android 资源目录。在新创建的资产文件夹中,您需要创建一个名为“tessdata”的常规目录,您可以在其中放置您的训练数据文件。 最后,您必须在 MainActivity 中初始化“TextExtractor”类。
final TextExtractor textExtractor = new TextExtractor(this, "tessdata", "eng");
第一个参数是上下文,第二个是刚刚创建的目录的名称,最后一个是刚刚下载的训练数据的语言。
要提取文本,您必须调用“extractText”方法:
final String text = textExtractor.extractText(imageWithText);
请注意,extractText 需要一个位图图像才能工作!! 您可以使用以下行从您的可绘制文件创建位图图像:
final BitMap image = BitmapFactory.decodeResource(getResources(), R.drawable.test_image);
如果您需要更多支持,我建议您遵循这个有用的指南:https://github.com/SamVanRoy/Android_OCR_App
【讨论】:
【参考方案20】:使用AssetManager,它允许读取资产中的文件。然后使用常规 Java IO 将文件写入 sdcard。
Google 是您的朋友,请搜索示例。
【讨论】:
【参考方案21】:这是迄今为止我在互联网上找到的最佳解决方案。 我使用了以下链接 https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217, 但它有一个错误,我修复了它,然后它就像一个魅力。 这是我的代码。你可以轻松使用它,因为它是一个独立的 java 类。
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/
。
您可以根据使用情况轻松找到需要对目录进行更改的部分。
如果您需要任何帮助,请随时联系我。
【讨论】:
【参考方案22】:您也可以使用 Guava 的ByteStream
将文件从 assets 文件夹复制到 SD 卡。这是我最终得到的解决方案,它将文件从资产文件夹递归复制到 SD 卡:
/**
* Copies all assets in an assets directory to the SD file system.
*/
public class CopyAssetsToSDHelper
public static void copyAssets(String assetDir, String targetDir, Context context)
throws IOException
AssetManager assets = context.getAssets();
String[] list = assets.list(assetDir);
for (String f : Objects.requireNonNull(list))
if (f.indexOf(".") > 1) // check, if this is a file
File outFile = new File(context.getExternalFilesDir(null),
String.format("%s/%s", targetDir, f));
File parentFile = outFile.getParentFile();
if (!Objects.requireNonNull(parentFile).exists())
if (!parentFile.mkdirs())
throw new IOException(String.format("Could not create directory %s.",
parentFile));
try (InputStream fin = assets.open(String.format("%s/%s", assetDir, f));
OutputStream fout = new FileOutputStream(outFile))
ByteStreams.copy(fin, fout);
else // This is a directory
copyAssets(String.format("%s/%s", assetDir, f), String.format("%s/%s", targetDir, f),
context);
【讨论】:
以上是关于如何将文件从“资产”文件夹复制到 SD 卡?的主要内容,如果未能解决你的问题,请参考以下文章
将目录和文件从 res/raw 文件夹复制到 sd 卡 - android
Android - 如何使用新的存储访问框架将文件复制到外部 sd 卡
将 /data/data 中的文件夹复制到 SD 卡,反之亦然