วิธีคัดลอกไฟล์จากโฟลเดอร์ 'ทรัพย์สิน' ไปยัง sdcard?


250

ฉันมีไฟล์สองสามไฟล์ในassetsโฟลเดอร์ ฉันต้องการคัดลอกทั้งหมดไปยังโฟลเดอร์ที่พูด / sdcard / โฟลเดอร์ ฉันต้องการทำสิ่งนี้จากภายในเธรด ฉันต้องทำอย่างไร?


คุณกำลังมองหาstackoverflow.com/questions/4447477/
DropAndTrap

2
ก่อนที่คุณจะคัดลอก / วางหนึ่งในโซลูชัน (ยอดเยี่ยม!) ด้านล่างลองใช้
ไลบรารี่

คำตอบ:


345

หากใครมีปัญหาเดียวกันนี่คือสิ่งที่ฉันทำ

private void copyAssets() {
    AssetManager assetManager = getAssets();
    String[] files = null;
    try {
        files = assetManager.list("");
    } catch (IOException e) {
        Log.e("tag", "Failed to get asset file list.", e);
    }
    if (files != null) for (String filename : files) {
        InputStream in = null;
        OutputStream out = null;
        try {
          in = assetManager.open(filename);
          File outFile = new File(getExternalFilesDir(null), filename);
          out = new FileOutputStream(outFile);
          copyFile(in, out);
        } catch(IOException e) {
            Log.e("tag", "Failed to copy asset file: " + filename, e);
        }     
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
        }  
    }
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
      out.write(buffer, 0, read);
    }
}

การอ้างอิง: ย้ายไฟล์โดยใช้ Java


28
ในการเขียนไฟล์ใน sdcard คุณจะต้องให้สิทธิ์ในรายการเช่น <ใช้สิทธิ์อนุญาต Android: ชื่อ = "android.permission.WRITE_EXTERNAL_STORAG ​​E ที่" />
IronBlossom

22
ฉันจะไม่พึ่งพา sdcard ที่ตั้งอยู่ที่ / sdcard แต่ดึงเส้นทางด้วย Environment.getExternalStorageDirectory ()
Axarydax

2
ฉันควรใช้: 16 * 1024 (16kb) ฉันมักจะเลือก 16K หรือ 32K เป็นสมดุลที่ดีระหว่างการใช้หน่วยความจำและประสิทธิภาพ
Nam Vu

3
@rciovati ได้รับข้อผิดพลาดรันไทม์นี้Failed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)
likejudo

7
สำหรับฉันรหัสนี้ใช้งานได้เฉพาะเมื่อฉันเพิ่มสิ่งนี้: in = assetManager.open("images-wall/"+filename);โดยที่ "images-wall" เป็นโฟลเดอร์ของฉันภายในเนื้อหา
Ultimo_m

62

ตามวิธีการแก้ปัญหาของคุณฉันทำบางอย่างของตัวเองเพื่ออนุญาตให้โฟลเดอร์ย่อย บางคนอาจพบว่ามีประโยชน์:

...

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)อาจช้าในอุปกรณ์เพื่อสร้างรายการเส้นทางสินทรัพย์ก่อนตัวอย่างนี้อาจใช้จากassetsdir:find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list
alexkasko

3
ทางออกที่ดี! การแก้ไขที่จำเป็นเพียงอย่างเดียวคือการตัดส่วนแยกนำที่จุดเริ่มต้นของ copyFileOrDir (): path = path.startsWith ("/")? path.substring (1): เส้นทาง;
Cross_


2
แทนที่ "/ data / data /" + this.getPackageName () ด้วย this.getFilesDir (). getAbsolutePath ()
ibrahimyilmaz

1
... และปิดสตรีมในfinallyบล็อก))
Mixaz

48

การแก้ปัญหาข้างต้นไม่ทำงานเนื่องจากข้อผิดพลาด:

  • การสร้างไดเรกทอรีไม่ทำงาน
  • เนื้อหาที่ส่งคืนโดย Android มีสามโฟลเดอร์ด้วย: รูปภาพเสียงและ webkit
  • เพิ่มวิธีจัดการกับไฟล์ขนาดใหญ่: เพิ่มนามสกุล. mp3 ไปยังไฟล์ในโฟลเดอร์ทรัพย์สินในโครงการของคุณและในระหว่างการคัดลอกไฟล์เป้าหมายจะไม่มีนามสกุล. mp3

นี่คือรหัส (ฉันออกจากบันทึกการใช้งาน แต่คุณสามารถวางได้ตอนนี้):

final static String TARGET_BASE_PATH = "/sdcard/appname/voices/";

private void copyFilesToSdCard() {
    copyFileOrDir(""); // copy all files in assets folder in my project
}

private void copyFileOrDir(String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        Log.i("tag", "copyFileOrDir() "+path);
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path);
        } else {
            String fullPath =  TARGET_BASE_PATH + path;
            Log.i("tag", "path="+fullPath);
            File dir = new File(fullPath);
            if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                if (!dir.mkdirs())
                    Log.i("tag", "could not create dir "+fullPath);
            for (int i = 0; i < assets.length; ++i) {
                String p;
                if (path.equals(""))
                    p = "";
                else 
                    p = path + "/";

                if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                    copyFileOrDir( p + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
}

private void copyFile(String filename) {
    AssetManager assetManager = this.getAssets();

    InputStream in = null;
    OutputStream out = null;
    String newFileName = null;
    try {
        Log.i("tag", "copyFile() "+filename);
        in = assetManager.open(filename);
        if (filename.endsWith(".jpg")) // extension was added to avoid compression on APK file
            newFileName = TARGET_BASE_PATH + filename.substring(0, filename.length()-4);
        else
            newFileName = TARGET_BASE_PATH + filename;
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", "Exception in copyFile() of "+newFileName);
        Log.e("tag", "Exception in copyFile() "+e.toString());
    }

}

แก้ไข: แก้ไขการวางผิดที่ ";" ที่กำลังขว้างข้อผิดพลาด "ไม่สามารถสร้าง dir" ได้อย่างเป็นระบบ


4
นี่จะต้องเป็นทางออก!
Massimo Variolo

1
หมายเหตุ: Log.i ("แท็ก", "ไม่สามารถสร้าง dir" + fullPath); มักจะเกิดขึ้นเป็น; ถูกใส่ผิดที่ถ้า
RoundSparrow hilltx

วิธีที่น่ากลัว! ขอบคุณมาก! แต่ทำไมคุณตรวจสอบไฟล์ jpg
Phuong

32

ฉันรู้ว่าสิ่งนี้ได้รับคำตอบแล้ว แต่ฉันมีวิธีที่สง่างามกว่าเล็กน้อยในการคัดลอกจากไดเรกทอรีสินทรัพย์ไปยังไฟล์บน sdcard มันไม่จำเป็นต้องใช้ "for" loop แต่ใช้ File Streams and Channels แทนเพื่อทำงาน

(หมายเหตุ) หากใช้ไฟล์บีบอัดชนิดใด APK, PDF, ... คุณอาจต้องการเปลี่ยนชื่อนามสกุลไฟล์ก่อนที่จะแทรกลงในเนื้อหาแล้วเปลี่ยนชื่อเมื่อคุณคัดลอกไปยัง SDcard)

AssetManager am = context.getAssets();
AssetFileDescriptor afd = null;
try {
    afd = am.openFd( "MyFile.dat");

    // Create new file to copy into.
    File file = new File(Environment.getExternalStorageDirectory() + java.io.File.separator + "NewFile.dat");
    file.createNewFile();

    copyFdToFile(afd.getFileDescriptor(), file);

} catch (IOException e) {
    e.printStackTrace();
}

วิธีการคัดลอกไฟล์โดยไม่ต้องวนซ้ำ

public static void copyFdToFile(FileDescriptor src, File dst) throws IOException {
    FileChannel inChannel = new FileInputStream(src).getChannel();
    FileChannel outChannel = new FileOutputStream(dst).getChannel();
    try {
        inChannel.transferTo(0, inChannel.size(), outChannel);
    } finally {
        if (inChannel != null)
            inChannel.close();
        if (outChannel != null)
            outChannel.close();
    }
}

ชอบสิ่งนี้มากกว่าโซลูชันอื่น ๆ นิดหน่อยเล็กน้อย การปรับเปลี่ยนเล็กน้อยในเหมืองที่รวมถึงการสร้าง fileFolders ที่ขาดหายไป ไชโย!
Chris.Jenkins

3
นี่ใช้ไม่ได้กับไฟล์ descriptor ของฉันเลยThis file can not be opened as a file descriptor; it is probably compressedมันเป็นไฟล์ pdf รู้วิธีการแก้ไขที่?
Gaʀʀʏ

1
สิ่งนี้จะถือว่า inChannel.size () คืนค่าขนาดของไฟล์ มันไม่รับประกันเช่นนั้น ฉันได้รับ 2.5 MiB สำหรับ 2 ไฟล์ที่แต่ละ 450 KiB
AI0867

1
ฉันเพิ่งพบว่า AssetFileDescriptor.getLength () จะส่งคืนขนาดไฟล์ที่ถูกต้อง
AI0867

1
นอกเหนือจากข้างต้นสินทรัพย์อาจไม่เริ่มต้นที่ตำแหน่ง 0 ในตัวอธิบายไฟล์ AssetFileDescriptor.getStartOffset () จะส่งคืนออฟเซ็ตเริ่มต้น
AI0867

5

ลองใช้มันง่ายกว่านี้จะช่วยให้คุณ:

// 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

นี่จะเป็นวิธีรัดกุมใน 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))
            }
        }
    }

รายการ (assetPath) ?. ให้ {... } ที่จริง มันน่าเบื่อ
Gábor

4

นี่คือเวอร์ชั่นที่ล้างแล้วสำหรับอุปกรณ์ Android ปัจจุบันการออกแบบวิธีการทำงานเพื่อให้คุณสามารถคัดลอกไปยังคลาส AssetsHelper เช่น;)

/**
 * 
 * Info: prior to Android 2.3, any compressed asset file with an
 * uncompressed size of over 1 MB cannot be read from the APK. So this
 * should only be used if the device has android 2.3 or later running!
 * 
 * @param c
 * @param targetFolder
 *            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

แก้ไขคำตอบSOนี้โดย @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());
    }

}

การเตรียมการ

ในsrc/main/assets เพิ่มโฟลเดอร์ด้วยชื่อfold

การใช้

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

ในไดเรกทอรีภายนอกค้นหาไฟล์และไดเรกทอรีทั้งหมดที่อยู่ในสินทรัพย์เท่า


3

คัดลอกไฟล์และไดเรกทอรีทั้งหมดจากทรัพย์สินไปยังโฟลเดอร์ของคุณ!

สำหรับการคัดลอกที่ดีกว่าให้ใช้ apache ทั่วไป io

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

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

}

// นี่คือวิธีการหลักในการทำสำเนา

private void doCopy(String dirName, String outPath) throws IOException {

    String[] srcFiles = assets.list(dirName);//for directory
    for (String srcFileName : srcFiles) {
        String outFileName = outPath + File.separator + srcFileName;
        String inFileName = dirName + File.separator + srcFileName;
        if (dirName.equals("")) {// for first time
            inFileName = srcFileName;
        }
        try {
            InputStream inputStream = assets.open(inFileName);
            copyAndClose(inputStream, new FileOutputStream(outFileName));
        } catch (IOException e) {//if directory fails exception
            new File(outFileName).mkdir();
            doCopy(inFileName, outFileName);
        }

    }
}

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

ตามคำตอบของ Yoram Cohen นี่คือรุ่นที่สนับสนุนไดเรกทอรีเป้าหมายที่ไม่ใช่แบบคงที่

ออกใบแจ้งหนี้ด้วยcopyFileOrDir(getDataDir(), "")เพื่อเขียนไปยังโฟลเดอร์ที่เก็บข้อมูลภายในแอพ / data / data / pkg_name /

  • รองรับโฟลเดอร์ย่อย
  • รองรับไดเรกทอรีเป้าหมายที่กำหนดเองและไม่คงที่
  • หลีกเลี่ยงการคัดลอกโฟลเดอร์ทรัพย์สินปลอมเช่น "images" ฯลฯ

    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

ใช้แนวคิดบางอย่างในคำตอบของคำถามนี้ฉันเขียนคลาสที่เรียกว่าAssetCopierการคัดลอก/assets/อย่างง่าย มันมีอยู่ในGitHubและสามารถเข้าถึงได้ด้วยjitpack.io :

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

ดูhttps://github.com/flipagram/android-assetcopierสำหรับรายละเอียดเพิ่มเติม


2

โดยพื้นฐานแล้วมีสองวิธีในการทำเช่นนี้

ก่อนอื่นคุณสามารถใช้AssetManager.openและตามที่อธิบายโดยRohith Nandakumarและทำซ้ำผ่านอินพุตสตรี

ประการที่สองคุณสามารถใช้AssetManager.openFdซึ่งช่วยให้คุณใช้FileChannel (ซึ่งมี [transferTo] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferTo(long , ยาว java.nio.channels.WritableByteChannel) และ [transferFrom] ( https://developer.android.com/reference/java/nio/channels/FileChannel.html#transferFrom(java.nio.channels.ReadableByteChannelยาว) ยาว)) วิธีการ) ดังนั้นคุณไม่ต้องวนซ้ำอินพุตสตรีมด้วยตัวคุณเอง

ฉันจะอธิบายวิธี openFd ที่นี่

การอัด

ก่อนอื่นคุณต้องแน่ใจว่าไฟล์นั้นถูกบีบอัดไว้ ระบบบรรจุภัณฑ์อาจเลือกที่จะบีบอัดไฟล์ใด ๆ ที่มีส่วนขยายที่ไม่ได้ทำเครื่องหมายเป็นnoCompressและไฟล์บีบอัดไม่สามารถแมปหน่วยความจำได้ดังนั้นคุณจะต้องพึ่งพาAssetManager.openในกรณีนั้น

คุณสามารถเพิ่มส่วนขยาย '.mp3' ลงในไฟล์ของคุณเพื่อหยุดการบีบอัด แต่วิธีแก้ไขที่เหมาะสมคือการแก้ไขไฟล์แอพ / build.gradleของคุณและเพิ่มบรรทัดต่อไปนี้ (เพื่อปิดการบีบอัดไฟล์ PDF)

aaptOptions {
    noCompress 'pdf'
}

การบรรจุไฟล์

โปรดทราบว่าผู้ทำแพ็กเกจยังคงสามารถบรรจุไฟล์หลาย ๆ ไฟล์เป็นไฟล์เดียวดังนั้นคุณไม่สามารถอ่านไฟล์ทั้งหมดที่AssetManagerให้มา คุณต้องถามAssetFileDescriptorว่าต้องการชิ้นส่วนใด

การค้นหาส่วนที่ถูกต้องของไฟล์ที่บรรจุ

เมื่อคุณได้มั่นใจไฟล์ของคุณจะถูกเก็บไว้ไม่มีการบีบอัด, คุณสามารถใช้AssetManager.openFdวิธีการที่จะได้รับAssetFileDescriptorซึ่งสามารถนำมาใช้เพื่อให้ได้FileInputStream (เหมือนAssetManager.openซึ่งส่งกลับInputStream ) ที่มีFileChannel นอกจากนี้ยังมีออฟเซ็ตเริ่มต้น (getStartOffset)และขนาด (getLength)ซึ่งคุณต้องได้รับส่วนที่ถูกต้องของไฟล์

การดำเนินงาน

ตัวอย่างการใช้งานได้รับด้านล่าง:

private void copyFileFromAssets(String in_filename, File out_file){
    Log.d("copyFileFromAssets", "Copying file '"+in_filename+"' to '"+out_file.toString()+"'");
    AssetManager assetManager = getApplicationContext().getAssets();
    FileChannel in_chan = null, out_chan = null;
    try {
        AssetFileDescriptor in_afd = assetManager.openFd(in_filename);
        FileInputStream in_stream = in_afd.createInputStream();
        in_chan = in_stream.getChannel();
        Log.d("copyFileFromAssets", "Asset space in file: start = "+in_afd.getStartOffset()+", length = "+in_afd.getLength());
        FileOutputStream out_stream = new FileOutputStream(out_file);
        out_chan = out_stream.getChannel();
        in_chan.transferTo(in_afd.getStartOffset(), in_afd.getLength(), out_chan);
    } catch (IOException ioe){
        Log.w("copyFileFromAssets", "Failed to copy file '"+in_filename+"' to external storage:"+ioe.toString());
    } finally {
        try {
            if (in_chan != null) {
                in_chan.close();
            }
            if (out_chan != null) {
                out_chan.close();
            }
        } catch (IOException ioe){}
    }
}

คำตอบนี้จะขึ้นอยู่กับคำตอบของ JPM


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

เปลี่ยนส่วนของรหัสดังนี้:

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

ตัวอย่างก่อนหน้านี้สำหรับไฟล์ PDF ในกรณีที่เป็นตัวอย่าง. txt

FileOutputStream fos = new FileOutputStream(file);

1

ใช้AssetManagerช่วยให้สามารถอ่านไฟล์ในเนื้อหา จากนั้นใช้ Java IO ปกติเพื่อเขียนไฟล์ไปยัง sdcard

Google เป็นเพื่อนของคุณค้นหาตัวอย่าง


1

สวัสดีพวกฉันทำบางสิ่งเช่นนี้ สำหรับการคัดลอกโฟลเดอร์และไฟล์ความลึก N-th เพื่อคัดลอก ซึ่งช่วยให้คุณสามารถคัดลอกโครงสร้างไดเรกทอรีทั้งหมดเพื่อคัดลอกจาก 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");
            }
        }
    }

ในท้ายที่สุดสร้าง Asynctask:

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

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

    }

เรียกมันว่าจากกิจกรรมของคุณ:

    new ManageAssetFolders().execute();

1

การแก้ไขเล็กน้อยของคำตอบข้างต้นเพื่อคัดลอกโฟลเดอร์ซ้ำและรองรับปลายทางที่กำหนดเอง

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

จากวิธีแก้ปัญหาของ Rohith Nandakumar ฉันได้ทำบางสิ่งเป็นของตัวเองเพื่อคัดลอกไฟล์จากโฟลเดอร์ย่อยของสินทรัพย์ (เช่น "assets / MyFolder ") นอกจากนี้ฉันกำลังตรวจสอบว่าไฟล์มีอยู่แล้วใน sdcard ก่อนที่จะพยายามคัดลอกอีกครั้ง

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

นี่คือทางออกที่ดีที่สุดที่ฉันสามารถหาได้บนอินเทอร์เน็ต ผมเคยใช้การเชื่อมโยงต่อไปนี้https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217 ,
แต่มันก็มีข้อผิดพลาดเดียวที่ฉันคงที่และแล้วมันทำงานเช่นเสน่ห์ นี่คือรหัสของฉัน คุณสามารถใช้มันได้อย่างง่ายดายเนื่องจากเป็นคลาส java อิสระ

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

อย่างที่คุณเห็นเพียงแค่สร้างตัวอย่าง CopyAssetsในคลาส java ของคุณที่มีกิจกรรม You cannot use AssetManager if the class has no activityตอนนี้ส่วนนี้มีความสำคัญเท่าที่ทดสอบของฉันและการวิจัยในอินเทอร์เน็ต มันมีบางอย่างที่เกี่ยวข้องกับบริบทของคลาส java
ตอนนี้c.copyAssets(getApplicationContext())เป็นวิธีที่ง่ายในการเข้าถึงวิธีการที่cเป็นและตัวอย่างของการCopyAssetsเรียน ตามความต้องการของฉันฉันอนุญาตให้โปรแกรมคัดลอกไฟล์ทรัพยากรทั้งหมดของฉันภายในassetโฟลเดอร์ไปยัง/www/resources/ไดเรกทอรีภายในของฉัน
คุณสามารถค้นหาส่วนที่คุณต้องการทำการเปลี่ยนแปลงในไดเรกทอรีตามการใช้งานของคุณ อย่าลังเลที่จะ ping ฉันหากคุณต้องการความช่วยเหลือ


0

สำหรับผู้ที่กำลังอัพเดทเป็น Kotlin:

ต่อไปนี้ขั้นตอนในการหลีกเลี่ยงFileUriExposedExceptionsการใช้งานเผื่อว่าได้รับได้รับอนุญาตและไฟล์ของคุณอยู่ในWRITE_EXTERNAL_STORAGEassets/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"
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.