Como copiar arquivos da pasta 'assets' para sdcard?
251
Eu tenho alguns arquivos no assets pasta Eu preciso copiar todos eles para uma pasta digamos / sdcard / folder. Eu quero fazer isso de dentro de um thread. Como eu faço isso?
Antes de copiar / colar uma das soluções a seguir, considere usar esta biblioteca para fazê-lo em uma linha de código: (grande!) Stackoverflow.com/a/41970539/9648
JohnnyLambada
Respostas:
346
Se alguém está tendo o mesmo problema, foi assim que eu fiz
para arquivos de gravação na sdcard você tem que dar a permissão no manifesto por exemplo <usos-permissão Android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />
IronBlossom
22
Eu também não iria contar com sdcard estar localizado em / sdcard, mas recuperar o caminho com Environment.getExternalStorageDirectory ()
Axarydax
2
Devo usar: 16 * 1024 (16kb) Eu tendem a optar por 16K ou 32K como um bom equilíbrio entre o uso da memória e o desempenho.
Nam Vu
3
@rciovati obteve este erro de tempo de execuçãoFailed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)
likejudo
7
Para mim, este código só funciona se eu acrescentar o seguinte: in = assetManager.open("images-wall/"+filename);onde "imagens-wall" é minha pasta dentro de ativos
Ultimo_m
62
Com base na sua solução, fiz algo próprio para permitir subpastas. Alguém pode achar isso útil:
assetManager.list(path)pode ser lento no dispositivo, para criar lista de ativos caminhos de antemão esse trecho pode ser usado a partir assetsdir:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
alexkasko
3
Ótima solução! A única correção necessária é aparar separadores à esquerda no início de copyFileOrDir (): path = path.startsWith ("/")? path.substring (1): caminho;
Cross_
Esse
fluxo de pilha
2
Substituir "/ data / data /" + this.getPackageName () com this.getFilesDir () getAbsolutePath ().
ibrahimyilmaz
1
... e fechar fluxos em finallybloco))
Mixaz 3/11
48
A solução acima não funcionou devido a alguns erros:
a criação do diretório não funcionou
os ativos retornados pelo Android também contêm três pastas: imagens, sons e webkit
Foi adicionada uma maneira de lidar com arquivos grandes: adicione a extensão .mp3 ao arquivo na pasta assets do seu projeto e, durante a cópia, o arquivo de destino ficará sem a extensão .mp3
Aqui está o código (deixei as instruções de log, mas você pode descartá-las agora):
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());}}
EDIT: corrigido um ";" extraviado que estava lançando um erro sistemático de "não foi possível criar o diretório".
NOTA: Log.i ("tag", "não pôde criar dir" + caminho completo); sempre acontece como; é extraviado no if.
RoundSparrow hilltx
maneira impressionante! Muito obrigado! Mas por que você verifica o arquivo jpg?
Phuong
32
Sei que isso foi respondido, mas tenho uma maneira um pouco mais elegante de copiar do diretório de ativos para um arquivo no sdcard. Ele não requer um loop "for", mas usa fluxos de arquivos e canais para fazer o trabalho.
(Nota) Se você estiver usando qualquer tipo de arquivo compactado, APK, PDF, ... poderá renomear a extensão do arquivo antes de inserir no ativo e renomear depois de copiá-lo para o cartão 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();}
Uma maneira de copiar um arquivo sem precisar percorrer o arquivo.
Gostei disso sobre as outras soluções, um pouco mais arrumado. Pequena modificação no meu que inclui a criação de pastas de arquivos ausentes. Felicidades!
Chris.Jenkins
3
Isso não funcionaria além do descritor de arquivo para mim, This file can not be opened as a file descriptor; it is probably compressed- é um arquivo pdf. Sabe como consertar isso?
Gaʀʀʏ
1
Isso pressupõe que inChannel.size () retorna o tamanho do tamanho do arquivo. Não faz tal garantia . Estou recebendo 2,5 MiB para 2 arquivos com 450 KiB cada.
AI0867
1
Acabei de descobrir que AssetFileDescriptor.getLength () retornará o tamanho de arquivo correto.
precisa saber é o seguinte
1
Além do acima, o ativo pode não iniciar no local 0 no descritor de arquivo. AssetFileDescriptor.getStartOffset () retornará o deslocamento inicial.
precisa saber é o seguinte
5
tente isso, é muito mais simples, isso ajudará você:
// 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) ?. deixe {...}, na verdade. É Anulável.
Gábor
4
Aqui está uma versão limpa para dispositivos Android atuais, design de método funcional para que você possa copiá-lo para uma classe AssetsHelper, por exemplo;)
/**
*
* 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();}
Usando alguns dos conceitos nas respostas a essa pergunta, escrevi uma classe chamada AssetCopierpara /assets/simplificar a cópia . Está disponível no github e pode ser acessado no jitpack.io :
Primeiro, você precisa garantir que o arquivo seja armazenado sem compactação. O sistema de empacotamento pode optar por compactar qualquer arquivo com uma extensão que não esteja marcada como noCompress e os arquivos compactados não podem ser mapeados na memória; portanto, você terá que confiar no AssetManager.open nesse caso.
Você pode adicionar uma extensão '.mp3' ao seu arquivo para impedir que ele seja compactado, mas a solução adequada é modificar o arquivo app / build.gradle e adicionar as seguintes linhas (para desativar a compactação de arquivos PDF)
aaptOptions {
noCompress 'pdf'}
Embalagem de arquivo
Observe que o empacotador ainda pode empacotar vários arquivos em um, então você não pode simplesmente ler o arquivo inteiro fornecido pelo AssetManager . Você precisa perguntar ao AssetFileDescriptor quais peças você precisa.
Localizando a parte correta do arquivo compactado
Depois de garantir que seu arquivo seja armazenado descompactado, você pode usar o método AssetManager.openFd para obter um AssetFileDescriptor , que pode ser usado para obter um FileInputStream (diferente do AssetManager.open , que retorna um InputStream ) que contém um FileChannel . Ele também contém o deslocamento inicial (getStartOffset) e o tamanho (getLength) , necessários para obter a parte correta do arquivo.
Oi pessoal, eu fiz algo assim. Para a N-ésima profundidade, copie a pasta e os arquivos para copiar. O que permite copiar toda a estrutura de diretórios do 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");}}}
Com base na solução de Rohith Nandakumar, fiz algo próprio para copiar arquivos de uma subpasta de ativos (por exemplo, "assets / MyFolder "). Além disso, estou verificando se o arquivo já existe no sdcard antes de tentar copiar novamente.
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);}}
Essa é, de longe, a melhor solução que consegui encontrar na internet. Usei o seguinte link https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 , mas houve um erro único que eu corrigi e funciona como um encanto. Aqui está o meu código. Você pode usá-lo facilmente, pois é uma classe java independente.
Como você pode ver, basta criar uma instância de CopyAssetsna sua classe java que tenha uma atividade. Agora essa parte é importante, tanto quanto meus testes e pesquisas na internet You cannot use AssetManager if the class has no activity,. Tem algo a ver com o contexto da classe java.
Agora, c.copyAssets(getApplicationContext())é uma maneira fácil de acessar o método, onde cestá e a instância da CopyAssetsclasse. Conforme minha exigência, permiti que o programa copiasse todos os meus arquivos de recursos dentro da assetpasta para o /www/resources/diretório interno.
Você pode descobrir facilmente a parte em que precisa fazer alterações no diretório conforme seu uso. Sinta-se à vontade para me enviar um ping se precisar de ajuda.
Após este passos para evitar FileUriExposedExceptions, o usuário supondo concedeu WRITE_EXTERNAL_STORAGEpermissão e seu arquivo está em 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"}
Respostas:
Se alguém está tendo o mesmo problema, foi assim que eu fiz
Referência: Mover arquivo usando Java
fonte
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);
onde "imagens-wall" é minha pasta dentro de ativosCom base na sua solução, fiz algo próprio para permitir subpastas. Alguém pode achar isso útil:
...
...
fonte
assetManager.list(path)
pode ser lento no dispositivo, para criar lista de ativos caminhos de antemão esse trecho pode ser usado a partirassets
dir:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
finally
bloco))A solução acima não funcionou devido a alguns erros:
Aqui está o código (deixei as instruções de log, mas você pode descartá-las agora):
EDIT: corrigido um ";" extraviado que estava lançando um erro sistemático de "não foi possível criar o diretório".
fonte
Sei que isso foi respondido, mas tenho uma maneira um pouco mais elegante de copiar do diretório de ativos para um arquivo no sdcard. Ele não requer um loop "for", mas usa fluxos de arquivos e canais para fazer o trabalho.
(Nota) Se você estiver usando qualquer tipo de arquivo compactado, APK, PDF, ... poderá renomear a extensão do arquivo antes de inserir no ativo e renomear depois de copiá-lo para o cartão SD)
Uma maneira de copiar um arquivo sem precisar percorrer o arquivo.
fonte
This file can not be opened as a file descriptor; it is probably compressed
- é um arquivo pdf. Sabe como consertar isso?tente isso, é muito mais simples, isso ajudará você:
fonte
Isso seria conciso em Kotlin.
fonte
Aqui está uma versão limpa para dispositivos Android atuais, design de método funcional para que você possa copiá-lo para uma classe AssetsHelper, por exemplo;)
fonte
Modificado esta resposta SO por @DannyA
Preparações
em
src/main/assets
adicionar pasta com nomefold
Uso
No diretório externo, localize todos os arquivos e diretórios que estão dentro dos ativos da dobra
fonte
Copie todos os arquivos e diretórios dos ativos para sua pasta!
para copiar melhor usar o apache commons io
// ESTE É O MÉTODO PRINCIPAL PARA CÓPIA
fonte
Com base na resposta de Yoram Cohen, aqui está uma versão que suporta diretório de destino não estático.
Invoque com
copyFileOrDir(getDataDir(), "")
para gravar na pasta de armazenamento do aplicativo interno / data / data / pkg_name /Evita copiar "imagens", etc., pastas de ativos falsas, como
fonte
Usando alguns dos conceitos nas respostas a essa pergunta, escrevi uma classe chamada
AssetCopier
para/assets/
simplificar a cópia . Está disponível no github e pode ser acessado no jitpack.io :Consulte https://github.com/flipagram/android-assetcopier para obter mais detalhes.
fonte
Existem essencialmente duas maneiras de fazer isso.
Primeiro, você pode usar o AssetManager.open e, conforme descrito por Rohith Nandakumar, e iterar no fluxo de entrada.
Segundo, você pode usar o AssetManager.openFd , que permite usar um FileChannel (que possui o [transferTo] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long , long, java.nio.channels.WritableByteChannel)) e [transferFrom] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel , long, long)) métodos), para que você não precise fazer um loop pelo fluxo de entrada.
Vou descrever o método openFd aqui.
Compressão
Primeiro, você precisa garantir que o arquivo seja armazenado sem compactação. O sistema de empacotamento pode optar por compactar qualquer arquivo com uma extensão que não esteja marcada como noCompress e os arquivos compactados não podem ser mapeados na memória; portanto, você terá que confiar no AssetManager.open nesse caso.
Você pode adicionar uma extensão '.mp3' ao seu arquivo para impedir que ele seja compactado, mas a solução adequada é modificar o arquivo app / build.gradle e adicionar as seguintes linhas (para desativar a compactação de arquivos PDF)
Embalagem de arquivo
Observe que o empacotador ainda pode empacotar vários arquivos em um, então você não pode simplesmente ler o arquivo inteiro fornecido pelo AssetManager . Você precisa perguntar ao AssetFileDescriptor quais peças você precisa.
Localizando a parte correta do arquivo compactado
Depois de garantir que seu arquivo seja armazenado descompactado, você pode usar o método AssetManager.openFd para obter um AssetFileDescriptor , que pode ser usado para obter um FileInputStream (diferente do AssetManager.open , que retorna um InputStream ) que contém um FileChannel . Ele também contém o deslocamento inicial (getStartOffset) e o tamanho (getLength) , necessários para obter a parte correta do arquivo.
Implementação
Um exemplo de implementação é dado abaixo:
Esta resposta é baseada na resposta do JPM .
fonte
altere partes do código como estas:
o exemplo anterior é para PDFs, no caso de .txt
fonte
Use o AssetManager , ele permite ler os arquivos nos ativos. Em seguida, use Java IO regular para gravar os arquivos no sdcard.
O Google é seu amigo, procure um exemplo.
fonte
Oi pessoal, eu fiz algo assim. Para a N-ésima profundidade, copie a pasta e os arquivos para copiar. O que permite copiar toda a estrutura de diretórios do Android AssetManager :)
No final, crie uma Asynctask:
chame-o De sua atividade:
fonte
Ligeira modificação da resposta acima para copiar uma pasta recursivamente e acomodar o destino personalizado.
fonte
Com base na solução de Rohith Nandakumar, fiz algo próprio para copiar arquivos de uma subpasta de ativos (por exemplo, "assets / MyFolder "). Além disso, estou verificando se o arquivo já existe no sdcard antes de tentar copiar novamente.
fonte
Essa é, de longe, a melhor solução que consegui encontrar na internet. Usei o seguinte link https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 ,
mas houve um erro único que eu corrigi e funciona como um encanto. Aqui está o meu código. Você pode usá-lo facilmente, pois é uma classe java independente.
Como você pode ver, basta criar uma instância de
CopyAssets
na sua classe java que tenha uma atividade. Agora essa parte é importante, tanto quanto meus testes e pesquisas na internetYou cannot use AssetManager if the class has no activity
,. Tem algo a ver com o contexto da classe java.Agora,
c.copyAssets(getApplicationContext())
é uma maneira fácil de acessar o método, ondec
está e a instância daCopyAssets
classe. Conforme minha exigência, permiti que o programa copiasse todos os meus arquivos de recursos dentro daasset
pasta para o/www/resources/
diretório interno.Você pode descobrir facilmente a parte em que precisa fazer alterações no diretório conforme seu uso. Sinta-se à vontade para me enviar um ping se precisar de ajuda.
fonte
Para quem está atualizando para o Kotlin:
Após este passos para evitar
FileUriExposedExceptions
, o usuário supondo concedeuWRITE_EXTERNAL_STORAGE
permissão e seu arquivo está emassets/pdfs/mypdf.pdf
.fonte