¿Cómo copiar archivos de la carpeta 'assets' a sdcard?
251
Tengo algunos archivos en la assetscarpeta. Necesito copiarlos a una carpeta, por ejemplo, / sdcard / folder. Quiero hacer esto desde un hilo. ¿Cómo lo hago?
Antes de copiar / pegar una de las (¡geniales!) Soluciones a continuación, considere usar esta biblioteca para hacerlo en una línea de código: stackoverflow.com/a/41970539/9648
JohnnyLambada
Respuestas:
346
Si alguien más tiene el mismo problema, así es como lo hice
para escribir archivos en la tarjeta sd debe dar permiso en el manifiesto, por ejemplo, <uses-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />
IronBlossom
22
Tampoco confiaría en la ubicación de la tarjeta
sd
2
¿Debo usar: 16 * 1024 (16kb)? Tiendo a optar por 16K o 32K como un buen equilibrio entre el uso de memoria y el rendimiento.
Nam Vu
3
@rciovati recibió este error de tiempo de ejecuciónFailed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)
likejudo
77
Para mí, este código solo funciona si agrego esto: in = assetManager.open("images-wall/"+filename);donde "images-wall" es mi carpeta dentro de los activos
Ultimo_m
62
Según su solución, hice algo por mi cuenta para permitir subcarpetas. Alguien puede encontrar esto útil:
assetManager.list(path)puede ser lento en el dispositivo, para crear una lista de rutas de activos de antemano, este fragmento se puede usar desde assetsdir:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
alexkasko
3
Buena solución! La única solución requerida es recortar los separadores iniciales al comienzo de copyFileOrDir (): path = path.startsWith ("/")? path.substring (1): ruta;
Cross_
Este stackoverflow está en ciertos dispositivos, por ejemplo: S5
Behelit
2
reemplace "/ data / data /" + this.getPackageName () con this.getFilesDir (). getAbsolutePath ()
ibrahimyilmaz
1
... y cerrar transmisiones en finallybloque))
Mixaz
48
La solución anterior no funcionó debido a algunos errores:
la creación del directorio no funcionó
los activos devueltos por Android contienen también tres carpetas: imágenes, sonidos y webkit
Forma agregada de manejar archivos grandes: Agregue la extensión .mp3 al archivo en la carpeta de activos en su proyecto y durante la copia, el archivo de destino no tendrá la extensión .mp3
Aquí está el código (dejé las declaraciones de Log pero ahora puedes descartarlas):
finalstaticString TARGET_BASE_PATH ="/sdcard/appname/voices/";privatevoid copyFilesToSdCard(){
copyFileOrDir("");// copy all files in assets folder in my project}privatevoid 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 =newFile(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);}}privatevoid copyFile(String filename){AssetManager assetManager =this.getAssets();InputStreamin=null;OutputStreamout=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=newFileOutputStream(newFileName);byte[] buffer =newbyte[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());}}
EDITAR: se corrigió un error ";" que arrojaba un error sistemático de "no se pudo crear el directorio".
NOTA: Log.i ("etiqueta", "no se pudo crear dir" + fullPath); siempre sucede como; está fuera de lugar en el if.
RoundSparrow hilltx
manera impresionante! ¡Muchas gracias! ¿Pero por qué revisas el archivo jpg?
Phuong
32
Sé que esto ha sido respondido, pero tengo una forma un poco más elegante de copiar desde el directorio de activos a un archivo en la tarjeta SD. No requiere un ciclo "for", sino que utiliza File Streams y Channels para hacer el trabajo.
(Nota) Si usa cualquier tipo de archivo comprimido, APK, PDF, ... puede cambiar el nombre de la extensión del archivo antes de insertarlo en el activo y luego cambiar el nombre una vez que lo copie en la tarjeta SD)
AssetManager am = context.getAssets();AssetFileDescriptor afd =null;try{
afd = am.openFd("MyFile.dat");// Create new file to copy into.File file =newFile(Environment.getExternalStorageDirectory()+ java.io.File.separator +"NewFile.dat");
file.createNewFile();
copyFdToFile(afd.getFileDescriptor(), file);}catch(IOException e){
e.printStackTrace();}
Una forma de copiar un archivo sin tener que recorrerlo.
Me gustó esto sobre las otras soluciones, un poco más ordenado. Ligera modificación en la mía que incluye la creación de carpetas de archivos faltantes. ¡salud!
Chris.Jenkins
3
Esto no funcionaría más allá del descriptor de archivo para mí, This file can not be opened as a file descriptor; it is probably compressedes un archivo pdf. ¿Sabes cómo arreglar eso?
Gaʀʀʏ
1
Esto supone que inChannel.size () devuelve el tamaño del tamaño del archivo. No hace tal garantía . Estoy obteniendo 2.5 MiB por 2 archivos que son 450 KiB cada uno.
AI0867
1
Acabo de descubrir que AssetFileDescriptor.getLength () devolverá el tamaño de archivo correcto.
AI0867
1
Además de lo anterior, el activo puede no comenzar en la ubicación 0 en el descriptor de archivo. AssetFileDescriptor.getStartOffset () devolverá el desplazamiento inicial.
AI0867
5
prueba esto es mucho más simple, esto te ayudará a:
// Open your local db as the input streamInputStream myInput = _context.getAssets().open(YOUR FILE NAME);// Path to the just created empty dbString outFileName =SDCARD PATH + YOUR FILE NAME;// Open the empty db as the output streamOutputStream myOutput =newFileOutputStream(outFileName);// transfer bytes from the inputfile to the outputfilebyte[] buffer =newbyte[1024];int length;while((length = myInput.read(buffer))>0){
myOutput.write(buffer,0, length);}// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
list (assetPath) ?. let {...}, en realidad. Es anulable.
Gábor
4
Aquí hay una versión limpia para dispositivos Android actuales, diseño de método funcional para que pueda copiarlo a una clase AssetsHelper, por ejemplo;)
/**
*
* 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)publicstaticboolean copyAssets(AssetManager assetManager,File targetFolder)throwsException{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
*/publicstaticboolean copyAssets(AssetManager assetManager,String path,File targetFolder)throwsException{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);returnfalse;}File targetDir =newFile(targetFolder, path);
targetDir.mkdirs();for(String source : sources){String fullSourcePath = path.equals("")? source :(path
+File.separator + source);
copyAssets(assetManager, fullSourcePath, targetFolder);}}returntrue;}privatestaticvoid copyAssetFileToFolder(AssetManager assetManager,String fullAssetPath,File targetBasePath)throwsIOException{InputStreamin= assetManager.open(fullAssetPath);OutputStreamout=newFileOutputStream(newFile(targetBasePath,
fullAssetPath));byte[] buffer =newbyte[16*1024];int read;while((read =in.read(buffer))!=-1){out.write(buffer,0, read);}in.close();out.flush();out.close();}
Utilizando algunos de los conceptos en las respuestas a esta pregunta, escribí una clase llamada AssetCopierpara simplificar la copia /assets/. Está disponible en github y se puede acceder con jitpack.io :
Primero debe asegurarse de que el archivo se almacene sin comprimir. El sistema de empaquetado puede optar por comprimir cualquier archivo con una extensión que no esté marcada como noCompress , y los archivos comprimidos no se pueden asignar en memoria, por lo que tendrá que confiar en AssetManager.open en ese caso.
Puede agregar una extensión '.mp3' a su archivo para evitar que se comprima, pero la solución adecuada es modificar su archivo app / build.gradle y agregar las siguientes líneas (para deshabilitar la compresión de archivos PDF)
aaptOptions {
noCompress 'pdf'}
Archivo de embalaje
Tenga en cuenta que el empaquetador aún puede empacar varios archivos en uno, por lo que no puede simplemente leer el archivo completo que le ofrece AssetManager . Debe preguntarle al AssetFileDescriptor qué partes necesita.
Encontrar la parte correcta del archivo empaquetado
Una vez que se haya asegurado de que su archivo se almacena sin comprimir, puede usar el método AssetManager.openFd para obtener un AssetFileDescriptor , que puede usarse para obtener un FileInputStream (a diferencia de AssetManager.open , que devuelve un InputStream ) que contiene un FileChannel . También contiene el desplazamiento inicial (getStartOffset) y el tamaño (getLength) , que necesita para obtener la parte correcta del archivo.
Implementación
A continuación se muestra un ejemplo de implementación:
Hola chicos, hice algo como esto. Para N-th Depth Copy Folder y archivos para copiar. Lo que le permite copiar toda la estructura de directorios para copiar desde Android AssetManager :)
privatevoid manageAssetFolderToSDcard(){try{String arg_assetDir = getApplicationContext().getPackageName();String arg_destinationDir =FRConstants.ANDROID_DATA + arg_assetDir;FileFolderInCache=newFile(arg_destinationDir);if(!FolderInCache.exists()){
copyDirorfileFromAssetManager(arg_assetDir, arg_destinationDir);}}catch(IOException e1){
e1.printStackTrace();}}publicString copyDirorfileFromAssetManager(String arg_assetDir,String arg_destinationDir)throwsIOException{File sd_path =Environment.getExternalStorageDirectory();String dest_dir_path = sd_path + addLeadingSlash(arg_destinationDir);File dest_dir =newFile(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 fileString 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;}publicvoid copyAssetFile(String assetFilePath,String destinationFilePath)throwsIOException{InputStreamin= getApplicationContext().getAssets().open(assetFilePath);OutputStreamout=newFileOutputStream(destinationFilePath);byte[] buf =newbyte[1024];int len;while((len =in.read(buf))>0)out.write(buf,0, len);in.close();out.close();}publicString addTrailingSlash(String path){if(path.charAt(path.length()-1)!='/'){
path +="/";}return path;}publicString addLeadingSlash(String path){if(path.charAt(0)!='/'){
path ="/"+ path;}return path;}publicvoid createDir(File dir)throwsIOException{if(dir.exists()){if(!dir.isDirectory()){thrownewIOException("Can't create directory, a file is in the way");}}else{
dir.mkdirs();if(!dir.isDirectory()){thrownewIOException("Unable to create directory");}}}
Basado en la solución de Rohith Nandakumar, hice algo para copiar archivos de una subcarpeta de activos (es decir, "activos / MyFolder "). Además, estoy comprobando si el archivo ya existe en sdcard antes de intentar copiarlo nuevamente.
privatevoid 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){InputStreamin=null;OutputStreamout=null;try{in= assetManager.open("MyFolder/"+filename);File outFile =newFile(getExternalFilesDir(null), filename);if(!(outFile.exists())){// File does not exist...out=newFileOutputStream(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}}}}}privatevoid copyFile(InputStreamin,OutputStreamout)throwsIOException{byte[] buffer =newbyte[1024];int read;while((read =in.read(buffer))!=-1){out.write(buffer,0, read);}}
Esta es, con mucho, la mejor solución que he podido encontrar en Internet. He usado el siguiente enlace https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 , pero tenía un solo error que solucioné y luego funciona de maravilla. Aquí está mi código. Puede usarlo fácilmente, ya que es una clase java independiente.
Como puede ver, simplemente cree una instancia de CopyAssetsen su clase java que tenga una actividad. Ahora bien, esta parte es importante, por lo que mis pruebas y la investigación en el Internet, You cannot use AssetManager if the class has no activity. Tiene algo que ver con el contexto de la clase java.
Ahora, c.copyAssets(getApplicationContext())es una manera fácil de acceder al método, donde cestá y la instancia de la CopyAssetsclase. Según mi requisito, permití que el programa copiara todos mis archivos de recursos dentro de la assetcarpeta en /www/resources/mi directorio interno.
Puede encontrar fácilmente la parte donde necesita hacer cambios en el directorio según su uso. Siéntase libre de enviarme un ping si necesita ayuda.
Siga estos pasos para evitar FileUriExposedExceptions, suponiendo que el usuario haya otorgado WRITE_EXTERNAL_STORAGEpermiso y que su archivo esté en assets/pdfs/mypdf.pdf.
private fun openFile(){var inputStream:InputStream?=nullvar outputStream:OutputStream?=nulltry{
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{privateconst val PDF_ASSETS_PATH ="pdfs"privateconst val PDF_FILE_NAME ="mypdf.pdf"}
Respuestas:
Si alguien más tiene el mismo problema, así es como lo hice
Referencia: Mover archivo usando Java
fuente
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);
donde "images-wall" es mi carpeta dentro de los activosSegún su solución, hice algo por mi cuenta para permitir subcarpetas. Alguien puede encontrar esto útil:
...
...
fuente
assetManager.list(path)
puede ser lento en el dispositivo, para crear una lista de rutas de activos de antemano, este fragmento se puede usar desdeassets
dir:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
finally
bloque))La solución anterior no funcionó debido a algunos errores:
Aquí está el código (dejé las declaraciones de Log pero ahora puedes descartarlas):
EDITAR: se corrigió un error ";" que arrojaba un error sistemático de "no se pudo crear el directorio".
fuente
Sé que esto ha sido respondido, pero tengo una forma un poco más elegante de copiar desde el directorio de activos a un archivo en la tarjeta SD. No requiere un ciclo "for", sino que utiliza File Streams y Channels para hacer el trabajo.
(Nota) Si usa cualquier tipo de archivo comprimido, APK, PDF, ... puede cambiar el nombre de la extensión del archivo antes de insertarlo en el activo y luego cambiar el nombre una vez que lo copie en la tarjeta SD)
Una forma de copiar un archivo sin tener que recorrerlo.
fuente
This file can not be opened as a file descriptor; it is probably compressed
es un archivo pdf. ¿Sabes cómo arreglar eso?prueba esto es mucho más simple, esto te ayudará a:
fuente
Esta sería una manera concisa en Kotlin.
fuente
Aquí hay una versión limpia para dispositivos Android actuales, diseño de método funcional para que pueda copiarlo a una clase AssetsHelper, por ejemplo;)
fuente
Modificó esta respuesta SO por @DannyA
Preparativos
en
src/main/assets
agregar carpeta con nombrefold
Uso
En el directorio externo, encuentre todos los archivos y directorios que están dentro de los activos de plegado
fuente
¡Copie todos los archivos y directorios de los activos a su carpeta!
para copiar mejor use apache commons io
// ESTE ES EL MÉTODO PRINCIPAL DE COPIA
fuente
Según la respuesta de Yoram Cohen, aquí hay una versión que admite un directorio de destino no estático.
Invoque con
copyFileOrDir(getDataDir(), "")
para escribir en la carpeta de almacenamiento de la aplicación interna / data / data / pkg_name /Evita copiar "imágenes", etc., carpetas de activos falsas como
fuente
Utilizando algunos de los conceptos en las respuestas a esta pregunta, escribí una clase llamada
AssetCopier
para simplificar la copia/assets/
. Está disponible en github y se puede acceder con jitpack.io :Consulte https://github.com/flipagram/android-assetcopier para obtener más detalles.
fuente
Básicamente, hay dos formas de hacer esto.
Primero, puede usar AssetManager.open y, como lo describe Rohith Nandakumar, e iterar sobre el flujo de entrada.
En segundo lugar, puede usar AssetManager.openFd , que le permite usar un FileChannel (que tiene [transferTo] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long , long, java.nio.channels.WritableByteChannel)) y [transferFrom] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel , long, largo)) métodos), por lo que no tiene que recorrer el flujo de entrada usted mismo.
Describiré el método openFd aquí.
Compresión
Primero debe asegurarse de que el archivo se almacene sin comprimir. El sistema de empaquetado puede optar por comprimir cualquier archivo con una extensión que no esté marcada como noCompress , y los archivos comprimidos no se pueden asignar en memoria, por lo que tendrá que confiar en AssetManager.open en ese caso.
Puede agregar una extensión '.mp3' a su archivo para evitar que se comprima, pero la solución adecuada es modificar su archivo app / build.gradle y agregar las siguientes líneas (para deshabilitar la compresión de archivos PDF)
Archivo de embalaje
Tenga en cuenta que el empaquetador aún puede empacar varios archivos en uno, por lo que no puede simplemente leer el archivo completo que le ofrece AssetManager . Debe preguntarle al AssetFileDescriptor qué partes necesita.
Encontrar la parte correcta del archivo empaquetado
Una vez que se haya asegurado de que su archivo se almacena sin comprimir, puede usar el método AssetManager.openFd para obtener un AssetFileDescriptor , que puede usarse para obtener un FileInputStream (a diferencia de AssetManager.open , que devuelve un InputStream ) que contiene un FileChannel . También contiene el desplazamiento inicial (getStartOffset) y el tamaño (getLength) , que necesita para obtener la parte correcta del archivo.
Implementación
A continuación se muestra un ejemplo de implementación:
Esta respuesta se basa en la respuesta de JPM .
fuente
cambiar partes de código como estos:
el ejemplo anterior es para Pdfs, en caso de ejemplo .txt
fuente
Usar AssetManager , permite leer los archivos en los activos. Luego use Java IO regular para escribir los archivos en sdcard.
Google es tu amigo, busca un ejemplo.
fuente
Hola chicos, hice algo como esto. Para N-th Depth Copy Folder y archivos para copiar. Lo que le permite copiar toda la estructura de directorios para copiar desde Android AssetManager :)
Al final, crea un Asynctask:
llámalo desde tu actividad:
fuente
Ligera modificación de la respuesta anterior para copiar una carpeta de forma recursiva y acomodar el destino personalizado.
fuente
Basado en la solución de Rohith Nandakumar, hice algo para copiar archivos de una subcarpeta de activos (es decir, "activos / MyFolder "). Además, estoy comprobando si el archivo ya existe en sdcard antes de intentar copiarlo nuevamente.
fuente
Esta es, con mucho, la mejor solución que he podido encontrar en Internet. He usado el siguiente enlace https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 ,
pero tenía un solo error que solucioné y luego funciona de maravilla. Aquí está mi código. Puede usarlo fácilmente, ya que es una clase java independiente.
Como puede ver, simplemente cree una instancia de
CopyAssets
en su clase java que tenga una actividad. Ahora bien, esta parte es importante, por lo que mis pruebas y la investigación en el Internet,You cannot use AssetManager if the class has no activity
. Tiene algo que ver con el contexto de la clase java.Ahora,
c.copyAssets(getApplicationContext())
es una manera fácil de acceder al método, dondec
está y la instancia de laCopyAssets
clase. Según mi requisito, permití que el programa copiara todos mis archivos de recursos dentro de laasset
carpeta en/www/resources/
mi directorio interno.Puede encontrar fácilmente la parte donde necesita hacer cambios en el directorio según su uso. Siéntase libre de enviarme un ping si necesita ayuda.
fuente
Para aquellos que están actualizando a Kotlin:
Siga estos pasos para evitar
FileUriExposedExceptions
, suponiendo que el usuario haya otorgadoWRITE_EXTERNAL_STORAGE
permiso y que su archivo esté enassets/pdfs/mypdf.pdf
.fuente