java.io.FileNotFoundException:此文件无法作为文件描述符打开;它可能被压缩了

问题描述 投票:0回答:14

我正在从 Android 编写音板。 问题是有些声音有效,有些则无效。 这是我得到的不起作用的声音的回溯

05-31 13:23:04.227 18440 18603 W System.err: java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed
05-31 13:23:04.227 18440 18603 W System.err:    at android.content.res.AssetManager.openAssetFd(Native Method)
05-31 13:23:04.227 18440 18603 W System.err:    at android.content.res.AssetManager.openFd(AssetManager.java:331)
05-31 13:23:04.227 18440 18603 W System.err:    at com.phonegap.AudioPlayer.startPlaying(AudioPlayer.java:201)
05-31 13:23:04.227 18440 18603 W System.err:    at com.phonegap.AudioHandler.startPlayingAudio(AudioHandler.java:181)
05-31 13:23:04.235 18440 18603 W System.err:    at com.phonegap.AudioHandler.execute(AudioHandler.java:64)
05-31 13:23:04.235 18440 18603 W System.err:    at com.phonegap.api.PluginManager$1.run(PluginManager.java:86)
05-31 13:23:04.235 18440 18603 W System.err:    at java.lang.Thread.run(Thread.java:1096)

有什么想法吗?

java android cordova filenotfoundexception
14个回答
117
投票

您可以禁用某些扩展的资源压缩,如下所示:

android {
    aaptOptions {
        noCompress "pdf"
    }
}

来源


52
投票

使用 Tensorflow Lite 文件的人们遇到了这个问题,

将以下行添加到 Gradle 文件 (

android/app/build.gradle
) 的
android{}
块内。

aaptOptions {
    noCompress "tflite"
}

45
投票

打开资源文件夹中的压缩文件有限制。这是因为未压缩的文件可以直接内存映射到进程虚拟地址空间,因此避免再次需要相同数量的内存来进行解压缩。

处理 Android 应用程序中的资产压缩讨论了处理压缩文件的一些技术。您可以使用未压缩的扩展名(例如

aapt
)欺骗
mp3
不压缩文件,或者您可以手动将它们添加到
apk
而不压缩,而不是让
aapt
来完成这项工作。


11
投票

您应该禁用该文件的压缩。只需添加:

    aaptOptions {
       noCompress "your-file-name"
    }

到您的应用程序级别

build.gradle
文件内
android { }


5
投票

出现这种明显令人恼火的情况是因为当构建

.apk
时,一些资产在存储之前被压缩,而其他资产则被视为已经压缩(例如图像、视频)并被单独保留。后一组可以使用
openAssetFd
打开,前一组不能 - 如果您尝试,您会收到“此文件无法作为文件描述符打开;它可能已被压缩”错误。

一种选择是欺骗构建系统不压缩资产(请参阅@nicstrong 的答案中的链接),但这很繁琐。最好尝试以更可预测的方式解决问题。

我提出的解决方案利用了这样一个事实:虽然您无法为资产打开

AssetFileDescriptor
,但您仍然可以打开
InputStream
。您可以使用它将资产复制到应用程序的文件缓存中,然后返回该资产的描述符:

@Override
public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) throws FileNotFoundException
{
    final String assetPath = uri.getLastPathSegment();  // or whatever

    try
    {
        final boolean canBeReadDirectlyFromAssets = ... // if your asset going to be compressed?
        if (canBeReadDirectlyFromAssets)
        {
            return getContext().getAssets().openFd(assetPath);
        }
        else
        {
            final File cacheFile = new File(getContext().getCacheDir(), assetPath);
            cacheFile.getParentFile().mkdirs();
            copyToCacheFile(assetPath, cacheFile);
            return new AssetFileDescriptor(ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY), 0, -1);
        }
    }
    catch (FileNotFoundException ex)
    {
        throw ex;
    }
    catch (IOException ex)
    {
        throw new FileNotFoundException(ex.getMessage());
    }
}

private void copyToCacheFile(final String assetPath, final File cacheFile) throws IOException
{
    final InputStream inputStream = getContext().getAssets().open(assetPath, ACCESS_BUFFER);
    try
    {
        final FileOutputStream fileOutputStream = new FileOutputStream(cacheFile, false);
        try
        {
            //using Guava IO lib to copy the streams, but could also do it manually
            ByteStreams.copy(inputStream, fileOutputStream); 
        }
        finally
        {
            fileOutputStream.close();
        }
    }
    finally
    {
        inputStream.close();
    }
}

这确实意味着您的应用程序将留下缓存文件,但这没关系。它也不会尝试重新使用现有的缓存文件,您可能关心也可能不关心。


5
投票

可以通过调用抛出此异常:

final AssetFileDescriptor afd = activity.getAssets().openFd(path);

我通过将文件保存在

res/raw
目录而不是资产文件夹中解决了问题,然后以这种方式获取
AssetFileDescriptor

final AssetFileDescriptor afd = activity.getResources().openRawResourceFd(rawId);

然后

FileNotFoundException
消失了,文件不再被压缩。


4
投票

仅当尝试打开

FileDesriptor
时,才会出现此异常。如果只是阅读文件,您可以直接浏览
InputStream
(
AssetManager.open("filename.ext")
)。这对我有用。

如果您提前需要文件大小,则需要

FileDescriptor
(因此是未压缩的文件)来调用其
getLength()
方法,否则您必须读取整个流才能确定其大小。


2
投票

我已经走了一圈,我使用:

ParcelFileDescriptor mFileDescriptor = context.getAssets().openFd(file).getParcelFileDescriptor();

但是返回:java.io.FileNotFoundException:此文件无法作为文件描述符打开;它可能被压缩了。

我没有使用此实现,而是直接使用 ParcelFileDescriptor 形式的函数打开文件。

private void openRenderer(Context context,String fileName) throws IOException {  

File file=  FileUtils.fileFromAsset(context, fileName);
        ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_WRITE); 

        mPdfRenderer = new PdfRenderer(parcelFileDescriptor);
    }`

    public class FileUtils {
    private FileUtils() {
    }

    public static File fileFromAsset(Context context, String assetName) throws IOException {
        File outFile = new File(context.getCacheDir(), assetName );
        copy(context.getAssets().open(assetName), outFile);

        return outFile;
    }

    public static void copy(InputStream inputStream, File output) throws IOException {
        FileOutputStream outputStream = null;

        try {
            outputStream = new FileOutputStream(output);
            boolean read = false;
            byte[] bytes = new byte[1024];

            int read1;
            while((read1 = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read1);
            }
        } finally {
            try {
                if(inputStream != null) {
                    inputStream.close();
                }
            } finally {
                if(outputStream != null) {
                    outputStream.close();
                }

            }

        }

    }
}

1
投票

刚刚在 build.gradle 中添加了我的文件的扩展名,如下所示并解决了我的问题

android {
   aaptOptions {
      noCompress "tflite"
      noCompress "txt"
      noCompress "pdf"
   }
}

0
投票

如果要从assets文件夹中获取的文件大于1MB,那么对我有用的方法是将文件压缩为zip文件,然后在使用前解压缩,并将其以未压缩的方式存储在外部存储中。

InputStream fileInputStream = getAssets().open("your_file.your_file_extension.zip");
unzipInputStream(fileInputStream, "your_folder_in_external_storage");

我用过的

unzipInputStream
方法是这个:

public static void unzipInputStream(InputStream inputStream, String location)
{
    try {
        if ( !location.endsWith(File.separator) ) {
            location += File.separator;
        }
        File f = new File(location);
        if(!f.isDirectory()) {
            f.mkdirs();
        }
        ZipInputStream zin = new ZipInputStream(new BufferedInputStream(inputStream, BUFFER_SIZE));
        try {
            ZipEntry ze;
            while ((ze = zin.getNextEntry()) != null) {
                String path = location + ze.getName();
                File unzipFile = new File(path);

                if (ze.isDirectory()) {
                    if(!unzipFile.isDirectory()) {
                        unzipFile.mkdirs();
                    }
                } else {
                    createParentDirectoriesIfMissing(unzipFile);
                    unzipFile(zin, unzipFile);
                }
            }
        } finally {
            zin.close();
        }
    } catch (Exception e) {
        Log.e("", "Unzip exception", e);
    }
}

private static void createParentDirectoriesIfMissing(File unzipFile)
{
    File parentDir = unzipFile.getParentFile();
    if ( null != parentDir ) {
        if ( !parentDir.isDirectory() ) {
            parentDir.mkdirs();
        }
    }
}

private static void unzipFile(ZipInputStream zin, File unzipFile) throws IOException
{
    int size;
    byte[] buffer = new byte[BUFFER_SIZE];
    FileOutputStream out = new FileOutputStream(unzipFile, false);
    BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);

    try {
        while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
            fout.write(buffer, 0, size);
        }

        zin.closeEntry();
    } finally {
        fout.flush();
        fout.close();
    }
}

0
投票

我刚刚遇到了同样的问题,因为 gnome-sound-recorder 创建了 OGG 文件,我无法使用 MediaPlayer 播放该文件。所以我用 ffmpeg 将它们转换为 MP3,它成功了。所以我想这是最简单的方法。

ffmpeg -i youroggfile yournewfile.mp3

我还注意到,它仍然在资源中显示为问号,并且当我使用 R.raw.yournewfile 访问它时,我不会在代码中写入“.mp3”扩展名。


0
投票

我遇到了同样的问题,我将文件从 res/raw 复制到 /data/data/your.app.pkg/cache 文件夹,然后一切顺利:D

AssetFileDescriptor afd = null;
try {
    File cache = new File(getCacheDir(), "my_data.dat");
    if (!cache.exists()) {
        copyInputStreamToFile(getResources().openRawResource(R.raw.my_data), cache);
    }
    afd = new AssetFileDescriptor(ParcelFileDescriptor.open(cache, ParcelFileDescriptor.MODE_READ_ONLY), 0, AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (Exception e) {
    e.printStackTrace();
}


private void copyInputStreamToFile(InputStream in, File file) {
    BufferedOutputStream bfos = null;

    try {
        bfos = new BufferedOutputStream(new FileOutputStream(file));
        byte[] buf = new byte[4096];

        int len;
        while ((len = in.read(buf)) != -1) {
            bfos.write(buf, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (bfos != null) {
                bfos.close();
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

0
投票

就我而言,它是由resource.arsc被压缩引起的。使用未压缩的resource.arsc重建可以解决问题。


0
投票

对于

kotlin dsl
,它看起来像这样:

    androidResources {
        noCompress.add("tflite")
    }
© www.soinside.com 2019 - 2024. All rights reserved.