web-dev-qa-db-ja.com

アセットフォルダーから1Mを超えるファイルを読み込む

私は夢中になります。ファイルオブジェクトを作成したので、ObjectInputStreamで読み取ることができ、アセットフォルダーを配置しました。この方法は、1Mより小さいファイルで機能し、大きいファイルではエラーが発生します。私はそれがAndroidプラットフォームの制限であると読みましたが、それは「簡単に」回避できることも知っています。たとえば、ゲームReging Thunderをダウンロードした人は、フォルダーは18.9Mのサイズのファイルです。これは、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();

圧縮されていないファイルがあり、「このファイルはファイル記述子として開くことができません。おそらく圧縮されています」というエラーを心配せずに使用できます。

この関数は、1Mより小さいファイルでうまく機能します。大きいファイルは、「ObjectInputStream ois = new ObjectInputStream(is);」行でJava.io.IOExceptionを返します。

なぜ??

36
Syco

同じ問題に直面しました。 4MBのファイルを1MBのチャンクに分割し、最初の実行時に、チャンクを電話のデータフォルダーに結合します。追加のボーナスとして、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();
}
48
Seva Alekseyev

制限は圧縮されたアセットにあります。アセットが圧縮されていない場合、システムはファイルデータをメモリマップし、Linux仮想メモリページングシステムを使用して、必要に応じて4Kチャンクをプルまたは破棄できます。 (「zipalign」ツールは、圧縮されていないアセットがファイル内でWordに揃えられることを保証します。つまり、直接マッピングされた場合、メモリ内でも揃えられます。)

資産が圧縮されている場合、システムはメモリ全体を圧縮解除する必要があります。 20MBのアセットがある場合、20MBの物理メモリがアプリケーションによって拘束されていることを意味します。

理想的には、システムは何らかの種類のウィンドウ圧縮を採用するため、一部のみが存在する必要がありますが、それにはアセットAPIとランダムアクセスで適切に機能する圧縮方式が必要です。現在、APK == Zipは「deflate」圧縮で圧縮されているため、実用的ではありません。

圧縮されないファイルタイプの接尾辞(たとえば、「。png」または「.mp3」)を付けることで、アセットを非圧縮状態に保つことができます。また、ビルドプロセス中に、それらをaaptにバンドルする代わりに、「Zip -0」を使用して手動で追加することもできます。これにより、APKのサイズが大きくなる可能性があります。

32
fadden

Sevaが提案したように、ファイルをチャンクに分割できます。これを使用して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()));

9
Oliver

ファイル拡張子を.mp3に変更します

7
yassine

私は別の解決策を見つけました。おそらくあなたはそれに興味があります。

ソースのルートで、build.xmlファイル、-package-resourcesのターゲットcustom_rules.xmlファイル。標準のAndroidアプリビルドシステムで何も壊さずに、antでターゲットを追加/変更するために使用されます。

このコンテンツでファイルを作成するだけです:

<?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
Ottavio Campana

これは古い質問ですが、良い解決策を考えました。事前に圧縮されたファイルをアセットフォルダーに保存しないのはなぜですか。その後、すでにZipファイルであるため圧縮されているため、再度圧縮する必要はありません。したがって、apkのサイズを小さくするためにファイルを圧縮したいが、ファイルの分割に対処したくない場合、これは簡単だと思います。

デバイスからそのファイルを読み取る必要がある場合は、入力ストリームをzipinputstreamにラップするだけです http://developer.Android.com/reference/Java/util/Zip/ZipInputStream.html

2
Matt Wolfe

プログラムを使用してデータベースを複数の部分に設定します(例:「Win Hex」)、 Link からダウンロードできます

続行 アセットフォルダーから1Mを超えるファイルを読み込む

0
Ehsan Jelodar

ファイル拡張子を追加するにはmp3を使用します。mydb.mp3inアセットフォルダーを使用し、.this runをerror.showなしでコピーして確認します。

0
santosh Kundkar

アセットフォルダーの代わりに、大きなファイルをrawフォルダーに入れました。わたしにはできる。

0
the100rabh

[〜#〜] gzip [〜#〜]を使用する方法もあります。ラップする必要があるのはInputStreamの中にGZIPInputStream

サイズ約3.0 MB、出力圧縮ファイルが約600KBのデータベースにこれを使用しました。

  • 最初にDBをコピーするために、 GZIPツール を使用してソース.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();
    }
0
VSB