Bagaimana cara menyalin file dari folder 'aset' ke sdcard?

251

Saya punya beberapa file di assetsfolder. Saya perlu menyalin semuanya ke folder say / sdcard / folder. Saya ingin melakukan ini dari dalam utas. Bagaimana saya melakukannya?

Rohith Nandakumar
sumber
Apakah Anda Mencari Ini stackoverflow.com/questions/4447477/…
DropAndTrap
2
Sebelum Anda menyalin / menempelkan salah satu (luar biasa) solusi di bawah ini, pertimbangkan untuk menggunakan perpustakaan ini untuk melakukannya dalam satu baris kode: stackoverflow.com/a/41970539/9648
JohnnyLambada

Jawaban:

346

Jika ada orang lain yang memiliki masalah yang sama, ini adalah bagaimana saya melakukannya

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

Referensi: Pindahkan file menggunakan Java

Rohith Nandakumar
sumber
28
untuk menulis file di sdcard Anda harus memberikan izin pada manifes misalnya <menggunakan-izin android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />
IronBlossom
22
Saya juga tidak akan bergantung pada sdcard yang berada di / sdcard, tetapi mengambil lintasan dengan Environment.getExternalStorageDirectory ()
Axarydax
2
Haruskah saya gunakan: 16 * 1024 (16kb) Saya cenderung memilih 16K atau 32K sebagai keseimbangan yang baik antara penggunaan memori dan kinerja.
Nam Vu
3
@rciovati mendapat kesalahan runtime iniFailed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)
likejudo
7
Bagi saya kode ini hanya berfungsi jika saya menambahkan ini: di in = assetManager.open("images-wall/"+filename);mana "gambar-dinding" adalah folder di dalam aset saya
Ultimo_m
62

Berdasarkan solusi Anda, saya melakukan sesuatu sendiri untuk memungkinkan subfolder. Seseorang mungkin menganggap ini bermanfaat:

...

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

}
DannyA
sumber
1
assetManager.list(path)mungkin lambat di perangkat, untuk membuat daftar jalur aset sebelumnya cuplikan ini dapat digunakan dari assetsdir:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
alexkasko
3
Solusi bagus! Satu-satunya perbaikan yang diperlukan adalah memotong pemisah utama di awal copyFileOrDir (): path = path.startsWith ("/")? path.substring (1): path;
Cross_
Alur
2
ganti "/ data / data /" + this.getPackageName () dengan this.getFilesDir (). getAbsolutePath ()
ibrahimyilmaz
1
... dan tutup stream di finallyblok))
Mixaz
48

Solusi di atas tidak berfungsi karena beberapa kesalahan:

  • pembuatan direktori tidak berfungsi
  • aset yang dikembalikan oleh Android juga mengandung tiga folder: gambar, suara, dan webkit
  • Cara yang ditambahkan untuk menangani file besar: Tambahkan ekstensi .mp3 ke file dalam folder aset di proyek Anda dan selama menyalin file target akan tanpa ekstensi .mp3

Ini kodenya (saya meninggalkan pernyataan Log tetapi Anda dapat menjatuhkannya sekarang):

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

}

EDIT: Mengoreksi tempat yang salah ";" yang melemparkan kesalahan sistematis "tidak dapat membuat dir".

Yoram Cohen
sumber
4
ini harus menjadi solusinya!
Massimo Variolo
1
CATATAN: Log.i ("tag", "tidak dapat membuat dir" + fullPath); selalu terjadi sebagai; salah tempat pada if.
RoundSparrow hilltx
cara awsome! Terima kasih banyak! Tapi mengapa Anda memeriksa file jpg?
Phuong
32

Saya tahu ini telah dijawab tetapi saya memiliki cara yang sedikit lebih elegan untuk menyalin dari direktori aset ke file di sdcard. Ini tidak memerlukan loop "for" tetapi sebaliknya menggunakan File Streams dan Channels untuk melakukan pekerjaan.

(Catatan) Jika menggunakan semua jenis file terkompresi, APK, PDF, ... Anda mungkin ingin mengubah nama ekstensi file sebelum memasukkan ke dalam aset dan kemudian mengganti nama setelah Anda menyalinnya ke 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();
}

Cara untuk menyalin file tanpa harus mengulanginya.

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();
    }
}
JPM
sumber
Menyukai ini di atas solusi lain, sedikit lebih rapi. Modifikasi sedikit pada saya yang termasuk membuat fileFolders hilang. Bersulang!
Chris. Jenkins
3
Ini tidak akan berhasil melewati deskriptor file untuk saya, This file can not be opened as a file descriptor; it is probably compressed- ini adalah file pdf. Tahu cara memperbaikinya?
Gaʀʀʏ
1
Ini mengasumsikan bahwa inChannel.size () mengembalikan ukuran ukuran file. Tidak ada jaminan seperti itu . Saya mendapatkan 2,5 MiB untuk 2 file masing-masing 450 KiB.
AI0867
1
Saya baru saja menemukan bahwa AssetFileDescriptor.getLength () akan mengembalikan filesize yang benar.
AI0867
1
Selain hal di atas, aset tidak boleh dimulai di lokasi 0 di deskriptor file. AssetFileDescriptor.getStartOffset () akan mengembalikan offset awal.
AI0867
5

coba ini lebih sederhana, ini akan membantu Anda:

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

Ini cara ringkas di 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))
            }
        }
    }
cchcc
sumber
daftar (assetPath) ?. biarkan {...} sebenarnya. Nullable.
Gábor
4

Ini adalah versi yang dibersihkan untuk perangkat Android saat ini, desain metode fungsional sehingga Anda dapat menyalinnya ke kelas AssetsHelper misalnya;)

/**
 * 
 * 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();
}
Simon
sumber
4

Dimodifikasi jawaban SO ini oleh @DannyA

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

}

Persiapan

di src/main/assets tambahkan folder dengan namafold

Pemakaian

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

Masuk ke direktori eksternal, temukan semua file dan direktori yang ada dalam aset lipat

Server web
sumber
3

Salin semua file dan direktori dari aset ke folder Anda!

untuk menyalin lebih baik gunakan apache commons io

public void doCopyAssets() throws IOException {
    File externalFilesDir = context.getExternalFilesDir(null);

    doCopy("", externalFilesDir.getPath());

}

// INI ADALAH METODE UTAMA UNTUK COPY

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);
    }
}
Serhii Bohutskyi
sumber
2

Berdasarkan jawaban Yoram Cohen, berikut adalah versi yang mendukung direktori target tidak statis.

Invoque dengan copyFileOrDir(getDataDir(), "")menulis ke folder penyimpanan aplikasi internal / data / data / pkg_name /

  • Mendukung subfolder.
  • Mendukung direktori target kustom dan non-statis
  • Hindari menyalin "gambar" dll seperti folder aset palsu

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

Menggunakan beberapa konsep dalam jawaban untuk pertanyaan ini, saya menulis sebuah kelas yang disebut AssetCopieruntuk membuat penyalinan menjadi /assets/sederhana. Ini tersedia di github dan dapat diakses dengan jitpack.io :

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

Lihat https://github.com/flipagram/android-assetcopier untuk detail lebih lanjut.

JohnnyLambada
sumber
2

Pada dasarnya ada dua cara untuk melakukan ini.

Pertama, Anda dapat menggunakan AssetManager.open dan, seperti yang dijelaskan oleh Rohith Nandakumar dan beralih ke inputstream.

Kedua, Anda dapat menggunakan AssetManager.openFd , yang memungkinkan Anda menggunakan FileChannel (yang memiliki [transferTo] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long , long, java.nio.channels.WritableByteChannel)) dan [transferFrom] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel , panjang, long)) metode), jadi Anda tidak perlu mengulangi arus input sendiri.

Saya akan menjelaskan metode openFd di sini.

Kompresi

Pertama, Anda perlu memastikan bahwa file tersebut disimpan tanpa terkompresi. Sistem pengemasan dapat memilih untuk mengompres file apa pun dengan ekstensi yang tidak ditandai sebagai noCompress , dan file yang dikompresi tidak dapat dipetakan dalam memori, jadi Anda harus mengandalkan AssetManager.open dalam kasus itu.

Anda dapat menambahkan ekstensi '.mp3' ke file Anda untuk menghentikannya agar tidak dikompres, tetapi solusi yang tepat adalah dengan memodifikasi file app / build.gradle Anda dan menambahkan baris berikut (untuk menonaktifkan kompresi file PDF)

aaptOptions {
    noCompress 'pdf'
}

Pengepakan file

Perhatikan bahwa pembuat paket masih dapat mengemas beberapa file menjadi satu, jadi Anda tidak bisa hanya membaca seluruh file yang diberikan AssetManager kepada Anda. Anda perlu bertanya pada AssetFileDescriptor bagian mana yang Anda butuhkan.

Menemukan bagian yang benar dari file yang dikemas

Setelah Anda memastikan file Anda disimpan tanpa kompresi, Anda dapat menggunakan metode AssetManager.openFd untuk mendapatkan AssetFileDescriptor , yang dapat digunakan untuk mendapatkan FileInputStream (tidak seperti AssetManager.open , yang mengembalikan InputStream ) yang berisi FileChannel . Ini juga berisi offset awal (getStartOffset) dan ukuran (getLength) , yang Anda perlukan untuk mendapatkan bagian file yang benar.

Penerapan

Contoh implementasi diberikan di bawah ini:

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

Jawaban ini didasarkan pada jawaban JPM .

AI0867
sumber
1
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);
        }
    }
}

ubah bagian kode seperti ini:

out = new BufferedOutputStream(new FileOutputStream(file));

contoh sebelum adalah untuk Pdf, untuk contoh .txt

FileOutputStream fos = new FileOutputStream(file);
Alex Zaraos
sumber
1

Gunakan AssetManager , memungkinkan untuk membaca file dalam aset. Kemudian gunakan Java IO biasa untuk menulis file ke sdcard.

Google adalah teman Anda, cari contoh.

Drakosha
sumber
1

Hai teman-teman, saya melakukan sesuatu seperti ini. Untuk Nth thth Copy Folder dan Files untuk menyalin. Yang Memungkinkan Anda untuk menyalin semua struktur direktori untuk disalin dari 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");
            }
        }
    }

Pada akhirnya, buat Asynctask:

    private class ManageAssetFolders extends AsyncTask<Void, Void, Void>
    {

        @Override
        protected Void doInBackground(Void... arg0)
        {
            manageAssetFolderToSDcard();
            return null;
        }

    }

sebut saja Dari aktivitas Anda:

    new ManageAssetFolders().execute();
DropAndTrap
sumber
1

Sedikit modifikasi jawaban di atas untuk menyalin folder secara rekursif dan untuk mengakomodasi tujuan kustom.

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);
}
Binoy Babu
sumber
1

Berdasarkan solusi Rohith Nandakumar, saya melakukan sesuatu sendiri untuk menyalin file dari subfolder aset (yaitu "aset / MyFolder "). Juga, saya memeriksa apakah file sudah ada di sdcard sebelum mencoba menyalin lagi.

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);
    }
}
Pablo Alfonso
sumber
0

Sejauh ini, ini adalah solusi terbaik yang dapat saya temukan di internet. Saya telah menggunakan tautan berikut https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 ,
tetapi ia memiliki satu kesalahan yang saya perbaiki dan kemudian berfungsi seperti pesona. Ini kode saya. Anda dapat dengan mudah menggunakannya karena ini adalah kelas java independen.

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

Seperti yang Anda lihat, cukup buat instance CopyAssetsdi kelas java Anda yang memiliki aktivitas. Sekarang bagian ini penting, sejauh pengujian dan penelitian saya di internet You cannot use AssetManager if the class has no activity,. Ini ada hubungannya dengan konteks kelas java.
Sekarang, c.copyAssets(getApplicationContext())ini adalah cara mudah untuk mengakses metode, di mana cdan contoh CopyAssetskelas. Sesuai kebutuhan saya, saya mengizinkan program untuk menyalin semua file sumber daya saya di dalam assetfolder ke /www/resources/direktori internal saya.
Anda dapat dengan mudah mengetahui bagian di mana Anda perlu membuat perubahan pada direktori sesuai penggunaan Anda. Jangan sungkan untuk menelepon saya jika Anda butuh bantuan.

Shan Singh
sumber
0

Bagi mereka yang memperbarui ke Kotlin:

Berikut ini langkah-langkah untuk menghindari FileUriExposedExceptions, seandainya pengguna telah diberikan WRITE_EXTERNAL_STORAGEizin dan file Anda di 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"
}
Óscar
sumber