Загрузка файлов размером более 1 м из папки ресурсов - PullRequest
36 голосов
/ 18 мая 2010

Я схожу с ума, я создал объект файла, чтобы его можно было прочитать с помощью ObjectInputStream, и поместил папку активов. Метод работает с файлом размером менее 1 МБ и выдает ошибку при работе с большими файлами. Я читал, что это предел платформы Android, но я также знаю, что этого можно «легко» избежать. Те, кто скачал игру Reging Thunder, например, могут легко увидеть, что в папке их ресурсов находится файл размером 18,9 МБ. Это мой код для чтения 1 объекта из ObjecInputStream

File f = File.createTempFile("mytempfile", "dat");
FileOutputStream fos = new FileOutputStream(f);

InputStream is = mc.getAssets().open(path,3);

ObjectInputStream ois=new ObjectInputStream(is);
byte[] data = (byte[]) ois.readObject();
fos.write(data);

fos.flush();
fos.close();
ois.close();
is.close();

теперь у меня есть несжатый файл, и я могу использовать его, не беспокоясь об ошибке «Этот файл нельзя открыть как дескриптор файла; возможно, он сжат»

Эта функция хорошо работает с файлами размером менее 1M, а файлы большего размера возвращают java.io.IOException в строке "ObjectInputStream ois = new ObjectInputStream (is);"

почему ??

Ответы [ 11 ]

48 голосов
/ 22 июня 2010

Столкнулся с той же проблемой. Я разделил файл размером 4 МБ на блоки размером 1 МБ, и при первом запуске я объединяю их в папку данных на телефоне. В качестве дополнительного бонуса, APK правильно сжимается. Файлы чанка называются 1.db, 2.db и т. Д. Код выглядит так:

File Path = Ctxt.getDir("Data", 0);
File DBFile = new File(Path, "database.db");

if(!DBFile.exists() || DatabaseNeedsUpgrade)  //Need to copy...
    CopyDatabase(Ctxt, DBFile);


static private void CopyDatabase(Context Ctxt, File DBFile) throws IOException
{
    AssetManager assets = Ctxt.getAssets();
    OutputStream outstream = new FileOutputStream(DBFile);
    DBFile.createNewFile();
    byte []b = new byte[1024];
    int i, r;
    String []assetfiles = assets.list("");
    Arrays.sort(assetfiles);
    for(i=1;i<10;i++) //I have definitely less than 10 files; you might have more
    {
        String partname = String.format("%d.db", i);
        if(Arrays.binarySearch(assetfiles, partname) < 0) //No such file in assets - time to quit the loop
            break;
        InputStream instream = assets.open(partname);
        while((r = instream.read(b)) != -1)
            outstream.write(b, 0, r);
        instream.close();
    }
    outstream.close();
}
32 голосов
/ 20 мая 2010

Ограничение на сжатые активы.Если ресурс является несжатым, система может отобразить в памяти данные файла и использовать систему подкачки виртуальной памяти Linux, чтобы извлекать или отбрасывать фрагменты 4K в зависимости от ситуации.(Инструмент «zipalign» гарантирует, что несжатые ресурсы будут выровнены по файлу в файле, что означает, что они также будут выровнены в памяти при непосредственном сопоставлении.)

Если ресурс сжат, система должна распаковатьвсе это на память.Если у вас есть 20 МБ актива, это означает, что 20 МБ физической памяти связано вашим приложением.

В идеале система должна использовать своего рода сжатие в виде окна, так что должны присутствовать только части, но для этого требуются некоторыеизящество в API актива и схема сжатия, которая хорошо работает с произвольным доступом.Прямо сейчас APK == Zip со сжатием "deflate", так что это не практично.

Вы можете сохранить свои ресурсы несжатыми, дав им суффикс типа файла, который не сжимается (например, ".png"или ".mp3").Вы также можете добавить их вручную во время процесса сборки с помощью «zip -0» вместо того, чтобы связывать их с помощью aapt.Это, вероятно, увеличит размер вашего APK.

9 голосов
/ 01 декабря 2010

Как сказал Сева, вы можете разбить файл на куски. Я использовал это, чтобы разделить мой файл 4MB

public static void main(String[] args) throws Exception {  
    String base = "tracks";  
    String ext = ".dat";  
    int split = 1024 * 1024;  
    byte[] buf = new byte[1024];  
    int chunkNo = 1;  
    File inFile = new File(base + ext);  
    FileInputStream fis = new FileInputStream(inFile);  
    while (true) {  
      FileOutputStream fos = new FileOutputStream(new File(base + chunkNo + ext));  
      for (int i = 0; i < split / buf.length; i++) {  
        int read = fis.read(buf);  
        fos.write(buf, 0, read);  
        if (read < buf.length) {  
          fis.close();  
          fos.close();  
          return;  
        }  
      }  
      fos.close();  
      chunkNo++;  
    }  
  }  

Если вам не нужно снова объединять файлы в один файл на устройстве, просто используйте этот InputStream, который объединяет их в один на лету.

import java.io.IOException;  
import java.io.InputStream;  

import android.content.res.AssetManager;  

public class SplitFileInputStream extends InputStream {  

  private String baseName;  
  private String ext;  
  private AssetManager am;  
  private int numberOfChunks;  
  private int currentChunk = 1;  
  private InputStream currentIs = null;  

  public SplitFileInputStream(String baseName, String ext, int numberOfChunks, AssetManager am) throws IOException {  
    this.baseName = baseName;  
    this.am = am;  
    this.numberOfChunks = numberOfChunks;  
    this.ext = ext;  
    currentIs = am.open(baseName + currentChunk + ext, AssetManager.ACCESS_STREAMING);  
  }  

  @Override  
  public int read() throws IOException {  
    int read = currentIs.read();  
    if (read == -1 && currentChunk < numberOfChunks) {  
      currentIs.close();  
      currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      return read();  
    }  
    return read;  
  }  

  @Override  
  public int available() throws IOException {  
    return currentIs.available();  
  }  

  @Override  
  public void close() throws IOException {  
    currentIs.close();  
  }  

  @Override  
  public void mark(int readlimit) {  
    throw new UnsupportedOperationException();  
  }  

  @Override  
  public boolean markSupported() {  
    return false;  
  }  

  @Override  
  public int read(byte[] b, int offset, int length) throws IOException {  
    int read = currentIs.read(b, offset, length);  
    if (read < length && currentChunk < numberOfChunks) {  
      currentIs.close();  
      currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      read += read(b, offset + read, length - read);  
    }  
    return read;  
  }  

  @Override  
  public int read(byte[] b) throws IOException {  
    return read(b, 0, b.length);  
  }  

  @Override  
  public synchronized void reset() throws IOException {  
    if (currentChunk == 1) {  
      currentIs.reset();  
    } else {  
      currentIs.close();  
      currentIs = am.open(baseName + currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      currentChunk = 1;  
    }  
  }  

  @Override  
  public long skip(long n) throws IOException {  
    long skipped = currentIs.skip(n);  
    if (skipped < n && currentChunk < numberOfChunks) {  
      currentIs.close();  
      currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING);  
      skipped += skip(n - skipped);  
    }  
    return skipped;  
  }  
}

Использование:
ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));

7 голосов
/ 01 сентября 2010

Изменить расширение файла на .mp3

2 голосов
/ 02 апреля 2013

Я нашел другое решение, может быть, оно вас заинтересовало.

В корне ваших источников, где у вас есть файл build.xml, вы можете перезаписать цель -package-resources в файле custom_rules.xml, который используется для добавления / изменения целей в ant, не нарушая ничего в стандарте система сборки приложений для Android.

Просто создайте файл с таким содержанием:

<?xml version="1.0" encoding="UTF-8"?>
<project name="yourAppHere" default="help">

    <target name="-package-resources" depends="-crunch">
        <!-- only package resources if *not* a library project -->
        <do-only-if-not-library elseText="Library project: do not package resources..." >
            <aapt executable="${aapt}"
                    command="package"
                    versioncode="${version.code}"
                    versionname="${version.name}"
                    debug="${build.is.packaging.debug}"
                    manifest="${out.manifest.abs.file}"
                    assets="${asset.absolute.dir}"
                    androidjar="${project.target.android.jar}"
                    apkfolder="${out.absolute.dir}"
                    nocrunch="${build.packaging.nocrunch}"
                    resourcefilename="${resource.package.file.name}"
                    resourcefilter="${aapt.resource.filter}"
                    libraryResFolderPathRefid="project.library.res.folder.path"
                    libraryPackagesRefid="project.library.packages"
                    libraryRFileRefid="project.library.bin.r.file.path"
                    previousBuildType="${build.last.target}"
                    buildType="${build.target}"
                    ignoreAssets="${aapt.ignore.assets}">
                <res path="${out.res.absolute.dir}" />
                <res path="${resource.absolute.dir}" />
                <nocompress /> <!-- forces no compression on any files in assets or res/raw -->
                <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
            </aapt>
        </do-only-if-not-library>
    </target>
</project>
2 голосов
/ 24 февраля 2012

Я знаю, что это старый вопрос, но я подумал о хорошем решении. Почему бы не сохранить файл, уже предварительно заархивированный в папке ресурсов. Тогда, поскольку это уже zip-файл и, следовательно, сжат, его не нужно будет снова сжимать. Поэтому, если вы хотите, чтобы файл был сжат, чтобы уменьшить размер вашего apk, но вы не хотите разбивать файлы, я думаю, что это проще.

Когда вам нужно прочитать этот файл с устройства, просто оберните входной поток в zipinputstream http://developer.android.com/reference/java/util/zip/ZipInputStream.html

0 голосов
/ 01 мая 2015

установить базу данных в несколько частей с помощью программы, например, "Win Hex", которую вы можете загрузить с Ссылка

и продолжить Загрузка файлов размером более 1М из папки ресурсов

0 голосов
/ 06 октября 2013

Использование GZIP будет другим методом. вам нужно только обернуть InputStream внутри GZIPInputStream .

Я использовал это для базы данных, размер которой составлял около 3,0 МБ, а выходной файл сжатия был около 600 КБ.

  • Для копирования БД при первом запуске я распаковал исходный файл .db, используя GZIP инструмент .
  • Затем переименовал его в .jpg, чтобы избежать большего сжатия (эти процессы выполняются до компиляции APK FILE).
  • Затем для чтения сжатого файла GZIP из ресурсов

и копирование его:

private void copydatabase() throws IOException {
        // Open your local db as the input stream
        InputStream myinput = mContext.getAssets().open(DB_NAME_ASSET);
        BufferedInputStream buffStream = new BufferedInputStream(myinput);
        GZIPInputStream zis = new GZIPInputStream(buffStream);

        // Path to the just created empty db
        String outfilename = DB_PATH + DB_NAME;

        // Open the empty db as the output stream
        OutputStream myoutput = new FileOutputStream(outfilename);


        // transfer byte to inputfile to outputfile
        byte[] buffer = new byte[1024];
        int length;
        while ((length = zis.read(buffer)) > 0) {
            myoutput.write(buffer, 0, length);
        }

        // Close the streams
        myoutput.flush();
        myoutput.close();
        zis.close();
        buffStream.close();
        myinput.close();
    }
0 голосов
/ 15 января 2013

добавить расширение файла mp3. Я использую папку mydb.mp3in assets и скопирую. Это без ошибок. Показываем его.

0 голосов
/ 20 мая 2010

Я использую NetBeans для сборки пакета и не нашел, как изменить настройки AAPT. Я не пробовал png, но mp3 сжаты. Я могу скомпилировать пакет и затем войти в папку активов с параметром -0? какую команду использовать правильно?

...