如何将文件从'资产'文件夹复制到SD卡?
assets
文件夹中有几个文件。 我需要将它们全部复制到一个文件夹说/ SD卡/文件夹。 我想从一个线程内做到这一点。 我该怎么做?
如果其他人有同样的问题,这是我怎么做的
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移动文件
基于你的解决scheme,我做了一些我自己的,允许子文件夹。 有人可能会觉得这有帮助:
…
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()); } }
上面的解决scheme由于一些错误而不起作用:
- 目录创build不起作用
- Android返回的资产还包含三个文件夹:图像,声音和webkit
- 增加处理大文件的方法:将扩展名.mp3添加到项目中assets文件夹中的文件中,并在复制期间将目标文件的扩展名为.mp3
这里是代码(我离开了日志的声明,但你可以放下它们):
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()); } }
编辑:更正了错位“;” 那就是抛出一个系统的“不能创造dir”的错误。
我知道这已被回答,但我有一个稍微更优雅的方式,从资产目录复制到SD卡上的文件。 它不需要“for”循环,而是使用文件stream和频道来完成这项工作。
(注)如果使用任何types的压缩文件,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(); } }
很好的例子。 回答了我如何访问资产文件夹中的文件的问题。
只有改变我会build议是在for循环。 以下格式也可以工作,是首选:
for(String filename : files) { InputStream in = null; OutputStream out = null; try { in = assetManager.open(filename); out = new FileOutputStream("/sdcard/" + filename); ... }
试试这简单得多,这将有助于你:
// 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();
这里是当前Android设备的清理版本,function方法devise,以便您可以将其复制到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 * eg {@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(); }
将资产中的所有文件和目录复制到您的文件夹!
对于复制更好的使用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); } } }
//这是来自commons IO
public static void closeQuietly(OutputStream output) { try { if(output != null) { output.close(); } } catch(IOException ioe) { //skip } } public static void closeQuietly(InputStream input) { try { if(input != null) { input.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); } }
通过@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());
在外部目录中查找折叠资产中的所有文件和目录
根据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()); } }
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);
使用AssetManager ,它允许读取资产中的文件。 然后使用常规Java IO将文件写入SD卡。
Google是你的朋友,search一个例子。
嗨,伙计们,我做了这样的事情。 对于第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"); } } }
最后创build一个Asynctask:
private class ManageAssetFolders extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... arg0) { manageAssetFolderToSDcard(); return null; } }
打电话给你从你的活动:
new ManageAssetFolders().execute();
上面的答案略有修改recursion复制一个文件夹,并容纳自定义的目的地。
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); }
基本上有两种方法可以做到这一点。
首先,您可以使用AssetManager.open ,如Rohith Nandakumar所述,并遍历inputstream。
其次,你可以使用AssetManager.openFd ,它允许你使用一个FileChannel (它有[transferTo]( 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,long,长))方法),所以你不必循环inputstream自己。
我将在这里描述openFd方法。
压缩
首先,您需要确保文件未压缩存储。 打包系统可以select使用未标记为noCompress的扩展名来压缩任何文件,并且压缩文件不能被映射到内存,所以在这种情况下您将不得不依赖于AssetManager.open 。
你可以在你的文件中添加一个'.mp3'扩展名来阻止它被压缩,但是正确的解决scheme是修改你的app / build.gradle文件并添加下面的行(禁止压缩PDF文件)
aaptOptions { noCompress 'pdf' }
文件打包
请注意,打包程序仍然可以打包多个文件,因此您不能只读取AssetManager提供的整个文件。 您需要询问AssetFileDescriptor您需要的零件。
find打包文件的正确部分
一旦确保文件未压缩存储,您可以使用AssetManager.openFd方法获取AssetFileDescriptor ,该文件可用于获取包含FileChannel的FileInputStream (与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){} } }
这个答案是基于JPM的回答 。
使用这个问题的答案中的一些概念,我写了一个名为AssetCopier
的类来复制/assets/
简单。 它在github上可用,可以通过jitpack.io访问:
new AssetCopier(MainActivity.this) .withFileScanning() .copy("tocopy", destDir);
有关更多详细信息,请参阅https://github.com/flipagram/android-assetcopier 。