java.io.FileNotFoundException:该文件不能作为文件描述符打开; 它可能是压缩的
我正在编程从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)
有任何想法吗?
在资源文件夹中打开压缩文件是有限制的。 这是因为未压缩的文件可以直接内存映射到进程的虚拟地址空间,因此避免了再次需要相同的内存量进行解压缩。
在Android应用程序中处理资产压缩讨论了处理压缩文件的一些技巧。 您可以通过使用未压缩的扩展名(例如mp3
)来诱骗aapt
压缩文件,或者您可以手动将它们添加到apk
而不进行压缩,而不是aapt
适应工作。
您可以禁用某些扩展的资产压缩,如下所示:
android { aaptOptions { noCompress "pdf" } }
资源
这个令人愤怒的情况出现了,因为当.apk
被创build时,一些资源在被存储之前被压缩,而其他资源则被视为已经被压缩(例如图像,video),并被单独保留。 后一组可以使用openAssetFd
打开,前一组不能 – 如果你尝试,你会得到“这个文件不能打开为文件描述符;它可能是压缩的”错误。
一种select是欺骗构build系统不压缩资产(请参阅@ nicstrong的答案中的链接),但这很麻烦。 最好以更可预测的方式尝试解决问题。
我所使用的解决scheme使用的事实是,虽然无法打开资产的AssetFileDescriptor
,但仍可以打开InputStream
。 您可以使用它将资产复制到应用程序的文件caching中,然后返回一个描述符:
@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(); } }
这确实意味着你的应用程序将离开caching文件,但没关系。 它也不会尝试重用现有的caching文件,您可能会也可能不会关心。
只有在尝试打开FileDesriptor
时才应该得到这个exception。 对于只读文件,您可以通过InputStream
( AssetManager.open("filename.ext")
)。 这对我有效。
如果您事先需要文件大小,则需要FileDescriptor
(因此是一个未压缩文件)来调用其getLength()
方法,否则您必须读取整个stream以确定其大小。
我走了一圈,我用:
ParcelFileDescriptor mFileDescriptor = context.getAssets().openFd(file).getParcelFileDescriptor();
但是,返回:java.io.FileNotFoundException:该文件不能作为文件描述符打开; 它可能是压缩的。
而不是这个实现我直接使用函数formsParcelFileDescriptor打开文件。
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(); } } } } }