Скопировать каталог из Активы в локальный каталог

Я пытаюсь использовать каталог, который у меня есть в папке с моими ресурсами, и обращаюсь к нему как к File. Возможно ли получить доступ к чему-либо в каталоге "Активы" как File? Если нет, как я могу скопировать каталог из папки "Активы" в локальный каталог приложения?

Я бы скопировал такой файл:

    try
    {
        InputStream stream = this.getAssets().open("myFile");
        OutputStream output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/myNewFile"));

        byte data[] = new byte[1024];
        int count;

        while((count = stream.read(data)) != -1)
        {
            output.write(data, 0, count);
        }

        output.flush();
        output.close();
        stream.close();
    }
    catch(IOException e)
    {
        e.printStackTrace();
    }

Однако я не уверен, как это сделать для каталога.

Я бы предпочел не строить свою инфраструктуру вокруг чего-то, что не работает, так как мне скопировать каталог из Assets в локальный каталог или можно получить доступ к каталогу в моих активах как File?

ИЗМЕНИТЬ

Вот как я решил это для моего собственного проекта:

InputStream stream = null;
OutputStream output = null;

for(String fileName : this.getAssets().list("demopass"))
{
    stream = this.getAssets().open("directoryName/" + fileName);
    output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/newDirectory/" + fileName));

    byte data[] = new byte[1024];
    int count;

    while((count = stream.read(data)) != -1)
    {
        output.write(data, 0, count);
    }

    output.flush();
    output.close();
    stream.close();

    stream = null;
    output = null;
}

Ответ 1

Как было предложено dmaxi в комментарии выше, вы можете использовать его ссылку с этим кодом:

    void displayFiles (AssetManager mgr, String path) {
        try {
            String list[] = mgr.list(path);
            if (list != null)
                for (int i=0; i<list.length; ++i)
                {
                    Log.v("Assets:", path +"/"+ list[i]);
                    displayFiles(mgr, path + "/" + list[i]);
                }
        } catch (IOException e) {
             Log.v("List error:", "can't list" + path);
        }
     }

Я взял его на эту ссылку. Возможно, вы можете комбинировать этот код с прецедентным.

EDIT: см. также AssetManager.

private void copyFolder(String name) {
            // "Name" is the name of your folder!
    AssetManager assetManager = getAssets();
    String[] files = null;

    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state)) {
        // We can read and write the media
        // Checking file on assets subfolder
        try {
            files = assetManager.list(name);
        } catch (IOException e) {
            Log.e("ERROR", "Failed to get asset file list.", e);
        }
        // Analyzing all file on assets subfolder
        for(String filename : files) {
            InputStream in = null;
            OutputStream out = null;
            // First: checking if there is already a target folder
            File folder = new File(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name);
            boolean success = true;
            if (!folder.exists()) {
                success = folder.mkdir();
            }
            if (success) {
                // Moving all the files on external SD
                try {
                    in = assetManager.open(name + "/" +filename);
                    out = new FileOutputStream(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
                    Log.i("WEBVIEW", Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
                    copyFile(in, out);
                    in.close();
                    in = null;
                    out.flush();
                    out.close();
                    out = null;
                } catch(IOException e) {
                    Log.e("ERROR", "Failed to copy asset file: " + filename, e);
                } finally {
                    // Edit 3 (after MMs comment)
                    in.close();
                    in = null;
                    out.flush();
                    out.close();
                    out = null;
                }
            }
            else {
                // Do something else on failure
            }       
        }
    } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        // We can only read the media
    } else {
        // Something else is wrong. It may be one of many other states, but all we need
        // is to know is we can neither read nor write
    }
}

// Method used by copyAssets() on purpose to copy a file.
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);
    }
}

EDIT 2:. Я добавил пример выше: этот фрагмент кода копирует только определенную папку из активов, на SD-карту. Дайте мне знать, если это сработает!

Ответ 2

Вы можете использовать следующий метод для копирования папки вашего ресурса в местоположение на SD-карте. Из вашего метода вызова просто вызовите moveAssetToStorageDir ("") для перемещения всей папки с данными. В случае подпапок вы можете указать относительный путь в папке с ресурсами.

public void moveAssetToStorageDir(String path){
    File file = getExternalFilesDir(null);
    String rootPath = file.getPath() + "/" + path;
    try{
        String [] paths = getAssets().list(path);
        for(int i=0; i<paths.length; i++){
            if(paths[i].indexOf(".")==-1){
                File dir = new File(rootPath + paths[i]);
                dir.mkdir();
                moveAssetToStorageDir(paths[i]);
            }else {
                File dest = null;
                InputStream in = null;
                if(path.length() == 0) {
                    dest = new File(rootPath + paths[i]);
                    in = getAssets().open(paths[i]);
                }else{
                    dest = new File(rootPath + "/" + paths[i]);
                    in = getAssets().open(path + "/" + paths[i]);
                }
                dest.createNewFile();
                FileOutputStream out = new FileOutputStream(dest);
                byte [] buff = new byte[in.available()];
                in.read(buff);
                out.write(buff);
                out.close();
                in.close();
            }
        }
    }catch (Exception exp){
        exp.printStackTrace();
    }
}

Ответ 3

Перемещение произвольной папки каталогов и файлов из активов

Дело в том, что... Активы особенные. Вы не можете обернуть его в объект File и спросить isDirectory(), и вы не сможете передать эти активы в NDK. Так что лучше их обернуть и переместить в каталог кеша или на SDCard, поэтому вы здесь.

Я видел много ответов SO, которые включают некоторую версию перекатывания массива строк fileOrDirectoryName, а затем создание каталогов, за которыми следует рекурсивный вызов и копирование отдельных файлов. Это приводит к созданию папки или файла, и вы не можете указать из актива, который у вас есть.

Сделать это Zip файлом

Моя рекомендация состоит в том, чтобы взять каждый произвольный набор активов, которые вы хотите отправить в SDCard или в папку внутреннего кэша, и закрепить его. Проблема структурирована таким образом, что более совместима с концепцией Assets.

AssetManager assetManager = context.getAssets();
String fullAssetPath = fromAssetPath + "/" + zipFilename;
String toPath = "/wherever/I/want";

try {
    InputStream inputStream = assetManager.open(fullAssetPath);
    ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));

    ZipEntry zipEntry;
    byte[] buffer = new byte[8192];
    while ((zipEntry = zipInputStream.getNextEntry()) != null) {
        String fileOrDirectory = zipEntry.getName();

        Uri.Builder builder = new Uri.Builder();
        builder.scheme("file");
        builder.appendPath(toPath);
        builder.appendPath(fileOrDirectory);
        String fullToPath = builder.build().getPath();

        if (zipEntry.isDirectory()) {
            File directory = new File(fullToPath);
            directory.mkdirs();
            continue;
        }   

        FileOutputStream fileOutputStream = new FileOutputStream(fullToPath);
        while ((count = zipInputStream.read(buffer)) != -1) {
            fileOutputStream.write(buffer, 0, count);
        }
        fileOutputStream.close();
        zipInputStream.closeEntry();
    }

    zipInputStream.close();

} catch (IOException e) {
    Log.e(TAG, e.getLocalizedMessage());
}

Небольшое примечание о размерах буфера

Я видел много примеров, связанных с очень маленькими размерами буфера, например 1024. Если вы просто не хотите тратить время, не стесняйтесь использовать более крупные размеры буфера. Даже мой выбор 8192, вероятно, невелик на современном оборудовании.

Избегание строгих путей

Обратите внимание на использование Uri.Builder для построения пути. Я предпочитаю этот стиль построения пути над directory + "/" + File. Тогда вы находитесь в бизнесе, ради согласованности, избегая назначения String d = "myDirectory/" или String f = "/file.txt" и других подобных хакерских взломов.

Ответ 4

Вот чистая версия ответа OP.

public void copyAssetFolderToFolder(Context activity, String assetsFolder, File destinationFolder) {
  InputStream stream = null;
  OutputStream output = null;
  try {
    for (String fileName : activity.getAssets().list(assetsFolder)) {
      stream = activity.getAssets().open(assetsFolder + ((assetsFolder.endsWith(File.pathSeparator))?"":File.pathSeparator) + fileName);
      output = new BufferedOutputStream(new FileOutputStream(new File(destinationFolder, fileName)));

      byte data[] = new byte[1024];
      int count;

      while ((count = stream.read(data)) != -1) {
        output.write(data, 0, count);
      }

      output.flush();
      output.close();
      stream.close();

      stream = null;
      output = null;
    }
  } catch (/*any*/Exception e){e.printStackTrace();}
}

Для справок в будущем, пожалуйста, сохраните всех и опубликуйте контекстно-зависимые списки источников. Этот сайт может стать отличным ресурсом кодирования для начинающих и экспертов, если только вы опубликуете полные ответы. Нельзя предположить, что кто-либо еще "понимает", где принадлежит случайный блок кода, или контекст, который должен выполнять код внутри.

Этот пример требует контекста активности, в котором находится метод getAssets(). В рамках платформы Android они представляют собой другие классы, помимо Activity, которые могут предоставить этот контекст. Одним из примеров является класс (общая ссылка) Service.

Ответ 5

Это код для папки копий с каталогом и файлами, которые копируются в папку sdcard... это отлично работает для меня...

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

}