Android:在ASSETS文件夹中读取GZIP文件

5
你如何在 Android 中读取位于“ASSETS”(或资源/原始)文件夹中的 GZIP 文件?
我尝试了以下代码,但我的流大小总是1。
GZIPInputStream fIn = new GZIPInputStream(mContext.getResources().openRawResource(R.raw.myfilegz)); 
int size = fIn.available();

由于某些原因,文件的大小始终为1。但是如果不使用GZIP压缩文件,则可以正常工作。

注意: 使用Android 1.5版本。


你有检查过文件大小吗?由于APK文件是ZIP档案,如果资源已经被压缩了,可能就没有必要再对其进行GZIP压缩。 - CommonsWare
回应CommonsWare的说法,我发现对资产进行gzip压缩对apk大小没有任何影响,因为apk已经被压缩了。不过,这是一个有趣的问题,为什么这样做不起作用(在我的测试中,我曾经让这种情况起到了作用)。 - Dan Lew
1
int size = fIn.available(); <- Java文档称:在EOF到达后返回0,否则始终返回1。 - fazo
6个回答

4

当我从资产文件夹读取gz文件时,遇到了同样的问题。

这是由于gz文件的文件名引起的。只需将yourfile.gz重命名为其他名称,例如yourfile.bin。如果Android构建系统认为它是gz文件,则似乎会自动解压缩文件。


1
实际上,它并不解压文件;它只是去掉了“.gz”扩展名。您仍然需要在输入流中包装一个GZIPInputStream才能读取它。 - Ted Hopp
1
仍然发生在最新的Android Studio上.. :P - Pasi Matalamäki

3
public class ResLoader {

    /**
     * @param res
     * @throws IOException
     * @throws FileNotFoundException
     * @throws IOException
     */

    static void unpackResources() throws FileNotFoundException, IOException {
        final int BUFFER = 8192;

        android.content.res.Resources t = TestingE3d.mContext.getResources();

        InputStream fis = t.openRawResource(R.raw.resources);
        if (fis == null)
            return;

        ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis,
                BUFFER));
        ZipEntry entry;
        while ((entry = zin.getNextEntry()) != null) {
            int count;

            FileOutputStream fos = TestingE3d.mContext.openFileOutput(entry
                    .getName(), 0);
            BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);

            byte data[] = new byte[BUFFER];

            while ((count = zin.read(data, 0, BUFFER)) != -1) {
                dest.write(data, 0, count);
                // Log.v("NOTAG", "writing "+count + " to "+entry.getName());
            }
            dest.flush();
            dest.close();
        }
        zin.close();

    }

}

R.raw.resources 是一个 zip 文件 - 这个类会将该 zip 中的所有文件解压到您的本地文件夹中。我在 NDK 中使用它。

您可以通过以下方式从 NDK 访问您的文件: /data/data//files/

包名 = ResLoader 所在的包 文件名 = raw/resources.zip 中的文件之一


2
这是InflaterInputStream.available方法的文档行为: http://java.sun.com/javase/6/docs/api/java/util/zip/InflaterInputStream.html#available()
Returns 0 after EOF has been reached, otherwise always return 1.

滥用available是一个常见的错误 --- 你不能假设它告诉你文件的长度(尽管有时候会这样做,正如你所注意到的)。你需要不断调用read(byte[], int, int)直到它返回0。如果你想要预先分配一个byte[]的长度,你可能需要创建一个ByteArrayOutputStream,并在每次读取时写入它,然后在退出循环时从中获取一个byte[]。这对于所有InputStreams在所有情况下都适用。


1
似乎构建系统将.gz文件视为特殊情况,即使它作为原始资源包含在内。将.gz文件重命名为具有不同扩展名的文件,例如.raw或.bin。

至少在Android Studio 2.2中有效。我找不到任何文档来确认这是预期行为或更好的方法来防止它,但至少更改扩展名可以解决问题。


0

如果使用 AssetManager 而不是 Resources,会发生什么?示例:

InputStream is = mContext.getAssets().open("myfilegz");
GZIPInputStream fIn = new GZIPINputStream(is);

在内部,Resources只是调用了AssetManager;我想知道是否在某个地方出现了问题。

0

试着查看Translate的源代码Android应用程序开源项目,看看是否有所帮助。

他们在selectRandomWord()函数[line 326]中使用GZIPInputStream来处理原始文件(以下是源代码)

public void selectRandomWord() {
    BufferedReader fr = null;
    try {
        GZIPInputStream is =
                new GZIPInputStream(getResources().openRawResource(R.raw.dictionary));

我一直收到FileNotFoundException的错误,这太荒谬了,因为资源知道文件在R.raw.myfile中。 - Tawani
这是个奇怪的问题。虽然不确定,但也许可以尝试在Eclipse中刷新你的项目并按照http://stackoverflow.com/questions/2322820/update-jar-used-by-android-app-in-eclipse/上的描述进行重新编译。 - snctln

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接