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

Я схожу с ума, я создал файловый объект, чтобы его можно было прочитать с помощью ObjectInputStream, и разместил папку с ресурсами. Этот метод работает с файлом размером менее 1M и дает ошибку с файлами большего размера. Я читал, что это предел платформы 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);»

Почему??


person Syco    schedule 18.05.2010    source источник


Ответы (11)


Столкнулся с той же проблемой. Я разделил свой файл размером 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();
}
person Seva Alekseyev    schedule 22.06.2010
comment
спасибо за ответ. Но какой инструмент вы используете, чтобы разрезать большие БД на более мелкие куски? - person anticafe; 23.11.2010
comment
Пробовал несколько готовых бесплатных программ, потом написал другое. :) Это 20-строчная фигура по прямой С. - person Seva Alekseyev; 23.11.2010
comment
Спасибо, Сева .. Петля у меня не заработала, но я сделал это вручную. У меня была база данных размером 5,3 МБ. Это сэкономило мне время. Спасибо еще раз. Для разделения базы данных я использовал инструмент HJSplit. - person Shraddha; 22.05.2012
comment
Все еще получаю IOException в Android 2.2 после разделения базы данных. Кто-нибудь может помочь. У меня есть база данных размером 6 МБ, и я не мог найти способ с ней работать. Спасибо - person djk; 02.04.2013
comment
2djk: задам отдельный вопрос. Это не форум, на вопрос ОП уже давно ответили. - person Seva Alekseyev; 02.04.2013
comment
Это все еще актуально для более новых версий Android? Я читал, что это нужно только для версий Android ‹2.3. - person smg; 09.06.2017
comment
Не совсем :))) - person Seva Alekseyev; 09.06.2017

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

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

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

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

person fadden    schedule 19.05.2010
comment
Я только что столкнулся с этой проблемой сегодня и мог заставить ее работать, изменив суффикс имени файла на .mp3, как было предложено. .png не работал. AssetManager сообщает, что не может найти файл :( Большое спасибо за подсказку о суффиксе! - person Francisco Junior; 22.03.2011
comment
в этом сообщении есть список такие расширения. - person Samuel; 11.04.2011

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

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

person Oliver    schedule 01.12.2010
comment
Кажется, ваш метод (ы) работает, но на практике я столкнулся с двумя проблемами. При первом методе (метод main - java - splitter) я сталкиваюсь с проблемой, когда файл был длиной точно 1024 * n (n ›0). И после того, как последние байты были прочитаны, int read имеет значение -1 и fos.write не удалось. Я изменил механизм записи вроде класса if (read > -1) { fos.write(buf, 0, read); }'. Next issue belongs to SplitFileInputStream`. Метод public int read(byte[] b) не работает. Поэтому я изменил его аналогично методу public int read(byte[] b, int offset, int length). - person zmeda; 14.06.2011
comment
Это похоже на public int read(byte[] b) throws IOException { int read = currentIs.read(b); if (read < b.length && currentChunk < numberOfChunks) { currentIs.close(); currentIs = am.open(baseName + ++currentChunk + ext, AssetManager.ACCESS_STREAMING); byte[] buffer = new byte[b.length - read]; read += read(buffer); } return read; } - person zmeda; 14.06.2011

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

person yassine    schedule 01.09.2010
comment
перевод: измените расширение файла на .mp3 - person eggie5; 04.08.2011
comment
хорошая работа, yassine, но с этим решением проблем в playStore нет (когда я загружаю apk в playStore) - person AITAALI_ABDERRAHMANE; 10.07.2013

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

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

person Matt Wolfe    schedule 24.02.2012
comment
Вот пример кода, как открыть zip-файл вместо gz-файла github.com/jgilfelt/android-sqlite-asset-helper/blob/master/ Кажется, это работает с файлами размером более 1M. - person Greg Dan; 11.12.2017

Нашел другое решение, может оно вам интересно.

В корне ваших источников, где у вас есть файл 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>
person Ottavio Campana    schedule 02.04.2013

добавляю расширение файла mp3. Я использую mydb.mp3 в папке с ресурсами и копирую. этот запуск без ошибок. покажите, проверьте это.

person santosh Kundkar    schedule 15.01.2013

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

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

  • Для копирования БД при первом запуске я сжал исходный файл .db с помощью инструмента GZIP.
  • Затем переименовал его в .jpg, чтобы избежать большего сжатия (эти процессы выполняются перед компиляцией ФАЙЛА APK).
  • Затем для чтения сжатого файла 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();
    }
person VSB    schedule 06.10.2013

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

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

person Ehsan Jelodar    schedule 01.05.2015

Вместо папки с ресурсами я поместил свои большие файлы в необработанную папку. Меня устраивает.

person the100rabh    schedule 19.05.2010
comment
У меня всегда одна и та же проблема: данные превышают UNCOMPRESS_DATA_MAX (1314625 против 1048576), все равно спасибо - person Syco; 19.05.2010
comment
Я использую Eclipse и свое приложение Navkar Matra androlib.com/android .application.com-app-navkarmantra-Cxin.aspx прекрасно работает с файлом размером 2 МБ. - person the100rabh; 21.05.2010

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

person Syco    schedule 20.05.2010