Làm cách nào để sao chép tệp từ thư mục 'tài sản' sang sdcard?


251

Tôi có một vài tập tin trong assetsthư mục. Tôi cần phải sao chép tất cả chúng vào một thư mục nói / sdcard / thư mục. Tôi muốn làm điều này từ trong một chủ đề. Tôi phải làm nó như thế nào?



2
Trước khi bạn sao chép / dán một trong những giải pháp (tuyệt vời!) Bên dưới, hãy cân nhắc sử dụng thư viện này để thực hiện trong một dòng mã: stackoverflow.com/a/41970539/9648
JohnnyLambada

Câu trả lời:


346

Nếu bất cứ ai khác có cùng một vấn đề, đây là cách tôi đã làm nó

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

Tham khảo: Di chuyển tệp bằng Java


28
để ghi các tệp trong sdcard, bạn phải cấp quyền cho tệp kê khai, ví dụ: <
used

22
Tôi cũng sẽ không dựa vào sdcard được đặt tại / sdcard, mà truy xuất đường dẫn với Môi
trường.getExternalStorageDirectory

2
Tôi nên sử dụng: 16 * 1024 (16kb) Tôi có xu hướng chọn 16K hoặc 32K như một sự cân bằng tốt giữa việc sử dụng bộ nhớ và hiệu suất.
Nam Vũ

3
@rciovati gặp lỗi thời gian chạy nàyFailed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)
likejudo

7
Đối với tôi mã này chỉ hoạt động nếu tôi thêm mã này: in = assetManager.open("images-wall/"+filename);trong đó "hình ảnh tường" là thư mục của tôi bên trong tài sản
Ultimo_m

62

Dựa trên giải pháp của bạn, tôi đã làm một cái gì đó của riêng tôi để cho phép các thư mục con. Ai đó có thể thấy điều này hữu ích:

...

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

}

1
assetManager.list(path)có thể bị chậm trên thiết bị, để tạo danh sách đường dẫn tài sản trước khi đoạn mã này có thể được sử dụng từ assetsthư mục:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
alexkasko

3
Giải pháp tốt đẹp! Cách khắc phục duy nhất được yêu cầu là cắt các dấu phân cách hàng đầu ở đầu copyFileOrDir (): path = path.startsWith ("/")? path.sub chuỗi (1): đường dẫn;
Cross_

Dòng stackover này trên một số thiết bị nhất định, ví dụ: S5
xử vào

2
thay thế "/ data / data /" + this.getPackageName () bằng this.getFilesDir (). getAbsolutePath ()
ibrahimyilmaz

1
... và đóng luồng trong finallykhối))
Mixaz

48

Giải pháp trên không hoạt động do một số lỗi:

  • tạo thư mục không hoạt động
  • tài sản được Android trả về cũng chứa ba thư mục: hình ảnh, âm thanh và webkit
  • Cách thêm vào để xử lý các tệp lớn: Thêm phần mở rộng .mp3 vào tệp trong thư mục tài sản trong dự án của bạn và trong khi sao chép tệp đích sẽ không có phần mở rộng .mp3

Đây là mã (Tôi đã để lại các báo cáo Nhật ký nhưng bạn có thể bỏ chúng ngay bây giờ):

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: Sửa lỗi đặt sai chỗ ";" đó là một lỗi "không thể tạo dir" có hệ thống.


4
điều này phải trở thành giải pháp!
Massimo Variolo

1
LƯU Ý: Log.i ("thẻ", "không thể tạo thư mục" + fullPath); luôn luôn xảy ra như; bị đặt sai chỗ nếu.
RoundSparrow hilltx

cách tuyệt vời! Cảm ơn rất nhiều! Nhưng tại sao bạn kiểm tra tập tin jpg?
Phương

32

Tôi biết điều này đã được trả lời nhưng tôi có một cách thanh lịch hơn một chút để sao chép từ thư mục tài sản vào một tệp trên sdcard. Nó không yêu cầu vòng lặp "for" mà thay vào đó sử dụng Luồng tệp và Kênh để thực hiện công việc.

(Lưu ý) Nếu sử dụng bất kỳ loại tệp nén, APK, PDF, ... bạn có thể muốn đổi tên phần mở rộng tệp trước khi chèn vào tài sản và sau đó đổi tên sau khi bạn sao chép nó vào 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();
}

Một cách để sao chép một tập tin mà không cần phải lặp qua nó.

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

Thích điều này hơn các giải pháp khác, gọn gàng hơn một chút. Sửa đổi nhẹ của tôi bao gồm việc tạo tập tin bị thiếu. chúc mừng
Chris.Jenkins

3
Điều này sẽ không hoạt động qua bộ mô tả tệp cho tôi, This file can not be opened as a file descriptor; it is probably compressed- đó là một tệp pdf. Biết cách khắc phục điều đó?
Gaʀʀʏ

1
Điều này giả định rằng inChannel.size () trả về kích thước của kích thước tệp. Nó làm cho không có đảm bảo như vậy . Tôi nhận được 2,5 MiB cho 2 tệp là 450 KiB mỗi tệp.
AI0867

1
Tôi vừa thấy rằng AssetFileDescriptor.getLdrops () sẽ trả về kích thước tệp chính xác.
AI0867

1
Ngoài những điều trên, tài sản có thể không bắt đầu ở vị trí 0 trong phần mô tả tệp. AssetFileDescriptor.getStart Offerset () sẽ trả về giá trị bù bắt đầu.
AI0867

5

Hãy thử nó đơn giản hơn nhiều, điều này sẽ giúp bạn:

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

5

Đây sẽ là cách ngắn gọn trong 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))
            }
        }
    }

danh sách (propertyPath) ?. hãy để {...}, thực sự. Không thể nào.
Gábor

4

Đây là phiên bản đã được dọn sạch cho các thiết bị Android hiện tại, thiết kế phương thức chức năng để bạn có thể sao chép nó vào lớp AssetsHelper, vd;)

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

4

Đã sửa đổi câu trả lời SO này bởi @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());
    }

}

Chuẩn bị

trong src/main/assets thư mục thêm tênfold

Sử dụng

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

Trong thư mục bên ngoài, tìm tất cả các tệp và thư mục nằm trong tài sản gấp


3

Sao chép tất cả các tập tin và thư mục từ tài sản vào thư mục của bạn!

để sao chép tốt hơn sử dụng apache commons io

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

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

}

// ĐÂY LÀ PHƯƠNG PHÁP CHÍNH CHO BẢN SAO

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

2

Dựa trên câu trả lời của Yoram Cohen, đây là phiên bản hỗ trợ thư mục đích không tĩnh.

Invoque với copyFileOrDir(getDataDir(), "")để ghi vào thư mục lưu trữ ứng dụng nội bộ / dữ liệu / dữ liệu / pkg_name /

  • Hỗ trợ các thư mục con.
  • Hỗ trợ thư mục đích tùy chỉnh và không tĩnh
  • Tránh sao chép "hình ảnh" vv các thư mục tài sản giả như

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

2

Sử dụng một số khái niệm trong câu trả lời cho câu hỏi này, tôi đã viết lên một lớp được gọi AssetCopierđể sao chép /assets/đơn giản. Nó có sẵn trên github và có thể được truy cập bằng jitpack.io :

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

Xem https://github.com/flipagram/android-assetcopier để biết thêm chi tiết.


2

Về cơ bản có hai cách để làm điều này.

Đầu tiên, bạn có thể sử dụng AssetManager.open và, như được mô tả bởi Rohith Nandakumar và lặp lại qua dòng đầu vào.

Thứ hai, bạn có thể sử dụng AssetManager.openFd , cho phép bạn sử dụng FileChannel (có [transferTo] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long , dài, java.nio.channels.WritableByteChannel)) và [transferFrom] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannel , dài, dài)) phương thức), vì vậy bạn không phải lặp lại luồng đầu vào.

Tôi sẽ mô tả phương pháp openFd ở đây.

Nén

Trước tiên, bạn cần đảm bảo rằng tệp được lưu trữ không nén. Hệ thống đóng gói có thể chọn nén bất kỳ tệp nào có phần mở rộng không được đánh dấu là noCompress và các tệp nén không thể được ánh xạ bộ nhớ, do đó bạn sẽ phải dựa vào AssetManager.open trong trường hợp đó.

Bạn có thể thêm tiện ích mở rộng '.mp3' vào tệp của mình để ngăn chặn nó bị nén, nhưng giải pháp thích hợp là sửa đổi tệp app / build.gradle của bạn và thêm các dòng sau (để tắt nén tệp PDF)

aaptOptions {
    noCompress 'pdf'
}

Đóng gói hồ sơ

Lưu ý rằng trình đóng gói vẫn có thể đóng gói nhiều tệp thành một, vì vậy bạn không thể đọc toàn bộ tệp mà AssetManager cung cấp cho bạn. Bạn cần hỏi AssetFileDescriptor những phần bạn cần.

Tìm phần chính xác của tệp được đóng gói

Một khi bạn đã chắc chắn tập tin của bạn được lưu trữ không nén, bạn có thể sử dụng AssetManager.openFd phương pháp để có được một AssetFileDescriptor , có thể được sử dụng để có được một FileInputStream (không giống như AssetManager.open , mà trả về một InputStream ) có chứa một FileChannel . Nó cũng chứa phần bù bắt đầu (getStart Offerset)kích thước (getLpm) , mà bạn cần để có được phần chính xác của tệp.

Thực hiện

Một ví dụ thực hiện được đưa ra dưới đây:

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

Câu trả lời này dựa trên câu trả lời của JPM .


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

thay đổi các phần của mã như thế này:

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

ví dụ trước là cho Pdf, trong trường hợp ví dụ .txt

FileOutputStream fos = new FileOutputStream(file);

1

Sử dụng AssetManager , nó cho phép đọc các tệp trong tài sản. Sau đó sử dụng Java IO thông thường để ghi các tệp vào sdcard.

Google là bạn của bạn, tìm kiếm một ví dụ.


1

Xin chào các bạn tôi đã làm một cái gì đó như thế này. Đối với thư mục và tệp sao chép độ sâu N-th để sao chép. Cho phép bạn sao chép tất cả cấu trúc thư mục để sao chép từ 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");
            }
        }
    }

Cuối cùng, tạo một Asynctask:

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

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

    }

gọi nó từ hoạt động của bạn:

    new ManageAssetFolders().execute();

1

Sửa đổi nhẹ câu trả lời ở trên để sao chép một thư mục đệ quy và để phù hợp với đích tùy chỉnh.

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

1

Dựa trên giải pháp của Rohith Nandakumar, tôi đã làm một cái gì đó của riêng mình để sao chép các tệp từ thư mục con của tài sản (ví dụ: "tài sản / MyFolder "). Ngoài ra, tôi đang kiểm tra xem tệp đã tồn tại trong sdcard hay chưa trước khi thử sao chép lại.

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

0

Đây là giải pháp tốt nhất tôi có thể tìm thấy trên internet. Tôi đã sử dụng liên kết sau https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 ,
nhưng nó đã có một lỗi mà tôi đã sửa và sau đó nó hoạt động như một bùa mê. Đây là mã của tôi. Bạn có thể dễ dàng sử dụng nó vì nó là một lớp java độc lập.

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

Như bạn có thể thấy, chỉ cần tạo một thể hiện CopyAssetstrong lớp java có hoạt động. Bây giờ phần này rất quan trọng, theo như thử nghiệm và nghiên cứu của tôi trên internet , You cannot use AssetManager if the class has no activity. Nó có một cái gì đó để làm với bối cảnh của lớp java.
Bây giờ, đây c.copyAssets(getApplicationContext())là một cách dễ dàng để truy cập phương thức, vị trí cvà thể hiện của CopyAssetslớp. Theo yêu cầu của tôi, tôi cho phép chương trình sao chép tất cả các tệp tài nguyên của tôi trong assetthư mục vào thư mục /www/resources/nội bộ của tôi.
Bạn có thể dễ dàng tìm ra phần mà bạn cần thay đổi thư mục theo mục đích sử dụng của bạn. Hãy ping tôi nếu bạn cần bất kỳ sự giúp đỡ.


0

Đối với những người đang cập nhật lên Kotlin:

Thực hiện theo các bước này để tránh FileUriExposedExceptions, giả sử người dùng đã cấp WRITE_EXTERNAL_STORAGEquyền và tệp của bạn nằm trong 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"
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.