如何复制文件'资产'文件夹到sdcard?

我在assets文件夹中有几个文件。我需要把它们都复制到一个文件夹,比如/sdcard/folder。我想从一个线程中做这件事。我该怎么做?

211480 次浏览

使用AssetManager,它允许读取资产中的文件。然后使用常规的Java IO将文件写入sdcard。

谷歌是你的朋友,搜索一个例子。

如果其他人也有同样的问题,我就是这么做的

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

参考:使用Java移动文件

基于您的解决方案,我自己做了一些事情来允许子文件夹。有些人可能会觉得这很有用:

...

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


}

由于一些错误,上述解决方案无法工作:

  • 目录创建失败
  • Android返回的资产也包含三个文件夹:图像,声音和webkit
  • 增加了处理大文件的方法:在项目的资产文件夹中添加扩展名.mp3到文件,在复制目标文件时将没有.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());
}


}

编辑:更正了一个错位的“;”,这是抛出一个系统的“无法创建目录”错误。

我知道这已经回答,但我有一个稍微更优雅的方式从资产目录复制到sdcard上的文件。它不需要“for”循环,而是使用文件流和通道来完成工作。

(注)如果使用任何类型的压缩文件,APK, PDF,…你可能想重命名文件扩展名之前插入到资产,然后重命名一旦你复制到SDcard)

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

试试这个,它更简单,这将帮助你:

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

复制所有文件和目录从资产到您的文件夹!

为了更好地复制,使用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);
}
}

基于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());
    }
    
    
    }
    
嗨,伙计们,我做了这样的事情。 用于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();

这里是一个针对当前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();
}
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));

前面的例子是针对pdf文件的,如果是。txt

FileOutputStream fos = new FileOutputStream(file);

轻微修改以上回答复制文件夹递归和适应自定义目的地。

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

由@ danya修改了所以的答案

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 < p > 添加名为fold

的文件夹

使用

File outDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
copyAssets("fold",outDir.toString());

在外部目录中找到折叠资产内的所有文件和目录

使用这个问题答案中的一些概念,我编写了一个名为AssetCopier的类,以使复制/assets/变得简单。它在github上可用,可以通过jitpack.io访问:

new AssetCopier(MainActivity.this)
.withFileScanning()
.copy("tocopy", destDir);

更多细节参见https://github.com/flipagram/android-assetcopier

基本上有两种方法。

首先,你可以使用AssetManager.open和,如Rohith Nandakumar所述,遍历输入流。

其次,你可以使用AssetManager.openFd,它允许你使用FileChannel(它有[transferTo](https://developer.android.com/reference/java/nio/channels/FileChannel.html transferTo(长, long, java.nio.channels.WritableByteChannel))和[transferFrom](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文件压缩)

aaptOptions {
noCompress 'pdf'
}

包装文件

请注意,打包器仍然可以将多个文件打包为一个文件,因此不能只读取AssetManager提供给你的整个文件。你需要问AssetFileDescriptor你需要哪些部件。

找到已打包文件的正确部分

一旦你确保你的文件是未压缩存储的,你可以使用AssetManager.openFd方法来获得一个AssetFileDescriptor,它可以用来获得一个包含FileChannelFileInputStream(不像AssetManager.open,它返回一个InputStream)。它还包含起始偏移量(getStartOffset)大小(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){}
}
}

这个答案基于摩根大通的回答

这是目前为止我在网上能找到的最好的解决方案。 我使用了下面的链接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())是访问该方法的一种简单方法,其中cCopyAssets类的实例。 根据我的要求,我允许程序将asset文件夹中的所有资源文件复制到我的内部目录的/www/resources/ 您可以很容易地找到需要根据您的使用对目录进行更改的部分。

.如果你需要帮助,请随时联系我

基于Rohith Nandakumar的解决方案,我自己做了一些事情,从资产的子文件夹中复制文件。“资产/ 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);
}
}

对于正在更新到Kotlin的用户:

遵循步骤避免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"
}

这在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))
}
}
}

你也可以使用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);
}
}
}


}

这就是我的个性化文本提取类,希望对大家有用。

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


要使用它,你需要训练过的数据文件。你可以从这个链接下载trainddata文件。

一旦你下载了你想要的traineddata文件,你需要在你的Android项目中创建一个名为assets的Android资源目录。在新创建的资产文件夹中,你需要创建一个名为“tessdata”的常规目录,在那里你可以放置你的训练数据文件。 最后,你必须初始化“textextractor”;

final TextExtractor textExtractor = new TextExtractor(this, "tessdata", "eng");

第一个参数是上下文,第二个参数是刚刚创建的目录名,最后一个参数是刚刚下载的训练数据的语言。

要提取文本,您必须调用“extracttext”;方法:

final String text = textExtractor.extractText(imageWithText);
注意extractText需要一个BitMap图像才能工作!! 你可以从你的可绘制文件中创建一个BitMap图像:

final BitMap image = BitmapFactory.decodeResource(getResources(), R.drawable.test_image);

如果你需要更多的支持,我建议你遵循这个有用的指南:https://github.com/SamVanRoy/Android_OCR_App

你可以用Kotlin在几个步骤中做到这一点,在这里我只复制几个文件,而不是所有从资产到我的应用程序文件目录。

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

在Kotlin中,这可以用一行完成!

为InputStream添加扩展乐趣

fun InputStream.toFile(to: File){
this.use { input->
to.outputStream().use { out->
input.copyTo(out)
}
}
}

然后使用它

MainActivity.kt

assets.open("test.zip").toFile(File(filesDir,"test.zip"))