从assets文件夹加载大于1M的文件

Syc*_*yco 36 android load assets inputstream objectinputstream

我疯了,我创建了一个文件对象,所以可以用ObjectInputStream读取它,然后放置了assets文件夹.该方法适用于小于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();
Run Code Online (Sandbox Code Playgroud)

现在我有一个未压缩的文件,我可以使用它而不用担心错误"这个文件不能作为文件描述符打开;它可能是压缩的"

此函数适用于小于1M的文件,较大的文件在"ObjectInputStream ois = new ObjectInputStream(is);"行返回java.io.IOException.

为什么??

Sev*_*yev 48

面对同样的问题.我已经将我的4MB文件切换成1 MB的块,并且在第一次运行时我将块加入到手机上的数据文件夹中.作为额外的奖励,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();
}
Run Code Online (Sandbox Code Playgroud)

  • 在linux和mac上有一个名为split的命令行工具. (6认同)

fad*_*den 32

限制是压缩资产.如果资产未压缩,则系统可以对文件数据进行内存映射,并使用Linux虚拟内存分页系统根据需要提取或丢弃4K块.("zipalign"工具确保未压缩的资源在文件中是字对齐的,这意味着它们在直接映射时也会在内存中对齐.)

如果资产被压缩,则系统必须将整个内容解压缩到内存中.如果您有20MB的资产,这意味着您的应用程序将占用20MB的物理内存.

理想情况下,系统将采用某种窗口压缩,因此只需要存在部分,但这需要资产API中的一些功能和适用于随机访问的压缩方案.现在APK ==拉链与"deflate"压缩,所以这是不切实际的.

您可以通过为资源提供未压缩的文件类型的后缀(例如".png"或".mp3")来保持资产未压缩.您也可以在构建过程中使用"zip -0"手动添加它们,而不是将它们捆绑在一起.这可能会增加APK的大小.

  • 这个[post](http://stackoverflow.com/questions/3177026/problem-while-opening-asset-file-with-the-help-of-content-provider)有一个这样的扩展列表. (2认同)

Oli*_*ver 9

像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++;  
    }  
  }  
Run Code Online (Sandbox Code Playgroud)

如果您不需要再次将文件合并到设备上的单个文件中,只需使用此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;  
  }  
}
Run Code Online (Sandbox Code Playgroud)

用法:
ObjectInputStream ois = new ObjectInputStream(new SplitFileInputStream("mytempfile", ".dat", 4, getAssets()));


小智 7

将文件扩展名更改为.mp3

  • 翻译:将文件扩展名更改为.mp3 (15认同)