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

发布于 2024-10-20 07:49:50 字数 721 浏览 6 评论 0原文

我快疯了,我创建了一个文件对象,这样就可以用 ObjectInputStream 读取它,并且我放置了资产文件夹。 该方法适用于小于 1M 的文件,对于较大的文件会出错。 我读到这是 Android 平台的限制,但我也知道可以“轻松”避免。 例如,下载过《雷霆迅雷》的玩家可以很容易地看到在他们的assets文件夹中有一个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,较大的文件返回 java.io.IOException 在线“ObjectInputStream ois=new ObjectInputStream(is);”

为什么??

I'm going crazy, I created a file object, so it can be read with ObjectInputStream, and I placed the assets folder.
The method works with a file smaller than 1M, and give error with larger files.
I read that is a limit of Android platform, but I also know that can be "easily" avoided.
Those who have downloaded the game Reging Thunder, for example, can easily see that in their assets folder is a file 18.9M large.
This is my code for read 1 object from a 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();

now I have an uncompressed file and I can use it without worrying about the error "This file can not be opened as a file descriptor; it is probably compressed"

This function works well with files smaller than 1M, with bigger files return an
java.io.IOException on line "ObjectInputStream ois=new ObjectInputStream(is);"

why??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

血之狂魔 2024-10-27 07:49:51

面临同样的问题。我已将 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();
}

Faced the same issue. I've cut up my 4MB file into 1 MB chunks, and on the first run I join the chunks into a data folder on the phone. As an added bonus, the APK is properly compressed. The chunk files are called 1.db, 2.db, etc. The code goes like this:

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();
}
只涨不跌 2024-10-27 07:49:51

限制在于压缩资产。如果资产未压缩,系统可以对文件数据进行内存映射,并使用 Linux 虚拟内存分页系统酌情拉入或丢弃 4K 块。 (“zipalign”工具确保未压缩的资源在文件中字对齐,这意味着直接映射时它们也会在内存中对齐。)

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

理想情况下,系统将采用某种窗口压缩,以便只需要存在某些部分,但这需要资产 API 中的一些奇特功能以及能够与随机访问配合良好的压缩方案。现在 APK == Zip 使用“deflate”压缩,所以这是不切实际的。

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

The limitation is on compressed assets. If the asset is uncompressed, the system can memory-map the file data and use the Linux virtual memory paging system to pull in or discard 4K chunks as appropriate. (The "zipalign" tool ensures that uncompressed assets are word-aligned in the file, which means they'll also be aligned in memory when directly mapped.)

If the asset is compressed, the system has to uncompress the entire thing to memory. If you have a 20MB asset, that means 20MB of physical memory is tied up by your application.

Ideally the system would employ some sort of windowed compression, so that only parts need to be present, but that requires some fanciness in the asset API and a compression scheme that works well with random access. Right now APK == Zip with "deflate" compression, so that's not practical.

You can keep your assets uncompressed by giving them a suffix of a file type that doesn't get compressed (e.g. ".png" or ".mp3"). You may also be able to add them manually during the build process with "zip -0" instead of having them bundled up by aapt. This will likely increase the size of your APK.

独留℉清风醉 2024-10-27 07:49:51

就像塞瓦建议的那样,您可以将文件分成多个块。我用它来分割我的 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()));

Like Seva suggested you can split up your file in chunks. I used this to split up my 4MB file

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++;  
    }  
  }  

If you do not need to combine the files into a single file on the device again, just use this InputStream, which combines them into one on the fly.

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;  
  }  
}

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

梦纸 2024-10-27 07:49:51

将文件扩展名更改为 .mp3

Change the file extension to .mp3

只有影子陪我不离不弃 2024-10-27 07:49:51

我知道这是一个老问题,但我想到了一个很好的解决方案。
为什么不将已经预压缩的文件存储在 asset 文件夹中。
然后,由于它已经是一个 zip 文件,因此已压缩,因此不需要再次压缩。因此,如果您希望压缩文件以减小 apk 的大小,但又不想处理拆分文件的问题,我认为这更容易。

当您需要从设备读取该文件时,只需将输入流包装在 zipinputstream 中
http://developer.android.com/reference/java/util/zip /ZipInputStream.html

I know this is an old question but I thought of a good solution.
Why not store the file already pre-zipped in the asset folder.
Then since it is already a zip file and therefore compressed it won't need to be compressed again. So if you wanted the file to be compressed to decrease the size of your apk but you don't want to deal with splitting up files I think this is easier.

When you need to read that file off the device just wrap the inputstream in a zipinputstream
http://developer.android.com/reference/java/util/zip/ZipInputStream.html

极度宠爱 2024-10-27 07:49:51

我找到了另一种解决方案,也许你对此感兴趣。

在源的根目录中,您有 build.xml 文件,您可以覆盖 custom_rules.xml 中的 -package-resources 目标> 文件,用于在 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>

I found another solution, maybe you're interested in it.

In the root of your sources, where you have the build.xml file, you can overwrite the -package-resources target in the custom_rules.xml file, which is used for adding/modifying targets in ant without breaking anything in the standard android app build system.

Just create a file with this content:

<?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>
嘿咻 2024-10-27 07:49:51

添加文件扩展名是 mp3。我在资产文件夹中使用 mydb.mp3 并复制。此运行没有错误。显示检查它。

add file extension is mp3.I use mydb.mp3in assets folder and copy .this run without error.show check it.

呆头 2024-10-27 07:49:51

使用GZIP将是另一种方法。您只需将 InputStream 包装在 内GZIPInputStream

我将其用于大小约为 3.0 MB 的数据库,输出压缩文件约为 600KB。

  • 为了在第一次运行中复制数据库,我使用 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();
    }

Using GZIP would be another method. you only need to wrap InputStream inside GZIPInputStream.

I used this for a database which size about 3.0 MB and output compress file was about 600KB.

  • For copying DB in firs run, I gzipped my source .db file using GZIP
    tool
    .
  • Then renamed it to .jpg in order to avoid more compression
    (these processes are done before compile APK FILE).
  • Then for reading compressed GZIP file from assetss

and copying it:

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();
    }
半岛未凉 2024-10-27 07:49:51

使用程序将数据库设置为多个部分,例如“Win Hex”,您可以从 链接

并继续从资源文件夹加载大于1M的文件

set database to multiple part with a program e.g "Win Hex", you can download from Link

and continue Load files bigger than 1M from assets folder

柳若烟 2024-10-27 07:49:51

我将大文件放在原始文件夹中,而不是资产文件夹中。这对我有用。

Instead of assets folder, I have put my large files in the raw folder. It works for me.

☆獨立☆ 2024-10-27 07:49:51

我使用 NetBeans 构建包,但没有找到如何更改 AAPT 的设置。
我没有尝试过 png,但 mp3 是压缩的。
我可以编译包然后进入assets文件夹,参数-0?
正确的命令是什么?

I use NetBeans to build the package and I not found how to change the settings of AAPT.
I have not tried the png, but the mp3 are compressed.
I can compile the package and then enter the assets folder with the parameter -0?
what would be the correct command to use?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文