我有一些文件在assets
文件夹中。我需要将它们全部复制到一个名为/sdcard/folder的文件夹中。我想要在一个线程内完成此操作。如何做到这一点?
我有一些文件在assets
文件夹中。我需要将它们全部复制到一个名为/sdcard/folder的文件夹中。我想要在一个线程内完成此操作。如何做到这一点?
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);
}
}
使用Kotlin可以轻松完成,这里我只复制了一些文件而不是全部从assets目录到我的应用程序文件目录。
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
根据Yoram Cohen的回答,这里提供一个支持非静态目录的版本。
使用copyFileOrDir(getDataDir(), "")
调用来将文件或目录写入内部应用存储文件夹 /data/data/pkg_name/。
Avoids copying "images" etc fake asset folders like
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());
}
}
这可以通过两种方式完成。
首先,您可以使用AssetManager.open方法,并像Rohith Nandakumar所描述的那样迭代输入流。
其次,您可以使用AssetManager.openFd方法,它允许您使用FileChannel(具有transferTo和transferFrom方法),因此您不必自己循环输入流。
我将在这里介绍openFd方法。
首先,您需要确保文件未经压缩存储。封装系统可能会选择压缩任何未标记为“noCompress”的扩展名的文件,而压缩文件无法映射到内存,因此您必须在这种情况下依靠AssetManager.open。
您可以将'.mp3'扩展名添加到文件中以防止对其进行压缩,但适当的解决方案是修改您的app/build.gradle文件并添加以下行(以禁用PDF文件的压缩)。
aaptOptions {
noCompress 'pdf'
}
请注意,打包程序仍然可以将多个文件打包成一个文件,因此您不能只读取AssetManager提供的整个文件。您需要询问AssetFileDescriptor需要哪些部分。
一旦您确保文件存储为未压缩状态,就可以使用AssetManager.openFd方法获取AssetFileDescriptor,该对象可用于获取包含FileChannel的FileInputStream(与返回InputStream的AssetManager.open不同)。它还包含起始偏移量(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){}
}
}
这个答案基于 JPM的回答。
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);
}
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"))
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");
}
}
}
private class ManageAssetFolders extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... arg0)
{
manageAssetFolderToSDcard();
return null;
}
}
从您的活动中调用它:
new ManageAssetFolders().execute();
对于那些正在更新到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"
}
FileOutputStream
时,flush()
是无用的,因为OutputStream
中flush()
的实现为空。
而且你也可以使用Stream.use{}
来省略对close
的调用。 - Zhou Hongbopackage 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文件。
一旦您下载了所需的训练数据文件,您需要在Android项目中创建一个名为“assets”的Android资源目录。在新创建的资产文件夹中,您需要创建一个名为“tessdata”的常规目录,您可以在其中放置训练数据文件。 最后,您需要在MainActivity中初始化“TextExtractor”类。
final TextExtractor textExtractor = new TextExtractor(this, "tessdata", "eng");
第一个参数是上下文,第二个参数是刚创建的目录名称,最后一个参数是刚下载的traineddata的语言。
要提取文本,您需要调用“extractText”方法:
final String text = textExtractor.extractText(imageWithText);
final BitMap image = BitmapFactory.decodeResource(getResources(), R.drawable.test_image);