2010-05-18 3 views
36

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

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

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

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

почему ??

ответ

48

Столкнулся с той же проблемой. Я разрезал свой файл размером 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(); 
} 
+1

спасибо за ответ. Но какой инструмент вы используете, чтобы вырезать свой большой db на каждый меньший кусок? – anticafe

+0

Пробовал несколько готовых бесплатных программ, затем написал другой. :) Это 20-строчный фрагмент в прямом C. –

+6

на linux и mac есть инструмент командной строки, называемый split. – mclin

0

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

+0

У меня всегда была одна и та же проблема: «Данные превышают UNCOMPRESS_DATA_MA X (1314625 vs 1048576) « спасибо в любом случае – Syco

+0

Я использую Eclipse и мое приложение Navkar Matra http://www.androlib.com/android.application.com-app-navkarmantra-Cxin.aspx прекрасно работает с файлом размером 2 МБ. – the100rabh

32

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

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

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

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

+0

Я сегодня столкнулся с этой проблемой и мог заставить ее работать, изменив суффикс имени файла на .mp3, как было предложено. .png не работает. AssetManager говорит, что не может найти файл :(Большое спасибо за подсказку суффикса! –

+2

это [сообщение] (http://stackoverflow.com/questions/3177026/problem-while-opening-asset-file-with- у вас есть список таких расширений. – Samuel

-1

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

7

ипа Methode па собственного достоинство чейнджер состо л ую Fichier продления TTF в mp3

+14

перевод: измените расширение файла на .mp3 – eggie5

+0

хорошая работа yassine, но с этим решением нет проблем на playStore (когда я загружаю apk на playStore) –

8

Как Сева предложил вам может разделите файл в кусках.Я использовал это, чтобы разделить мой 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()));

+0

Ваш метод (ы) кажется но на практике я столкнулся с двумя проблемами. Сначала метод (main - java - splitter method) сталкивается с проблемой, когда файл имеет ровно 1024 * n (n> 0) long. И после того, как последние байты были прочитаны, 'int read' имеет значение' -1' и 'fos.write' не удалось. Я изменил механизм записи, например 'if (read> -1) {fos.write (buf, 0, read); }». Следующий выпуск относится к классу «SplitFileInputStream». Метод 'public int read (byte [] b)' не работает. Поэтому я поменял его так же, как 'public int read (byte [] b, int offset, int length)' method. – zmeda

+0

Это выглядит как 'public int read (byte [] b) throws IOException { \t \t int read = currentIs.read (b); \t \t if (читать zmeda

2

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

Когда вам нужно прочитать файл выключения устройства просто обернуть InputStream в ZipInputStream папки mp3.I использовать mydb.mp3in активов http://developer.android.com/reference/java/util/zip/ZipInputStream.html

+0

Вот пример кода, как открыть zip-файл вместо gz-файла https://github.com/jgilfelt/android-sqlite-asset-helper/blob/master/library/src/main/java/com/ readystatesoftware/sqliteasset/SQLiteAssetHelper.java # L449 Это похоже на работу с файлами размером более 1 МБ. –

0

расширения добавить файл и скопировать .Это работать без проверки error.show Это.

+0

спасибо, что это очень простой трюк, который работает – Jack

2

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

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

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

<?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> 
0

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

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

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

и скопировать его:

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(); 
    } 
Смежные вопросы