Android ContentProvider openFile:需要服务"修改"文件

时间:2014-12-21 17:12:41

标签: java android android-contentprovider

我想通过ContentProvider提供保存在“外部存储空间”的图像文件。

这些图像文件被“损坏” - 前50个字节与一些任意值进行异或。我希望在ContentProvider内做“demangle”,以便其他应用程序不需要进行特殊处理。

我正在使用Mininum SDK 14版。

这是我的第一次尝试 - 使用管道ParcelFileDescriptor

public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    // basic uri/mode check here
    return openPipeHelper(uri, getType(uri), null, new FileInputStream(getImageFile(uri)), new PipeDataWriter<InputStream>() {
        @Override
        public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, final String mimeType, Bundle opts, InputStream input) {
            InputStream fin = new FilterInputStream(input) {
                private int cnt = 0;
                private byte mask;

                @Override
                public int read() throws IOException {
                    byte[] buffer = new byte[1];
                    return read(buffer) == -1 ? -1 : (buffer[0] & 0xff);
                }

                @Override
                public int read(@NonNull byte[] buffer) throws IOException {
                    return read(buffer, 0, buffer.length);
                }

                @Override
                public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
                    int ret = super.read(buffer, byteOffset, byteCount);
                    if (ret <= 0) return ret;
                    if (cnt == 0) {
                        switch (mimeType) {
                            case "image/png":
                                mask = (byte) (buffer[byteOffset] ^ 137);
                                break;
                            case "image/webp":
                                mask = (byte) (buffer[byteOffset] ^ 'R');
                                break;
                        }
                    }
                    for (int i = byteOffset; i < byteOffset + ret && cnt < 50; i++, cnt++) {
                        buffer[i] ^= mask;
                    }
                    return ret;
                }
            };
            OutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(output);
            byte[] buf = new byte[1024 * 1024];
            try {
                while (true) {
                    int n = fin.read(buf);
                    if (n == -1) break;
                    Log.i(TAG, "openFile get n=" + n);
                    fout.write(buf, 0, n);
                    fout.flush();
                }
            } catch (IOException ex) {
                // EPIPE likely means pipe closed on other end; treat it as WAI.
                if (!ex.getMessage().contains("EPIPE")) {
                    Log.w(TAG, "openFile failed", ex);
                }
            } finally {
                try {
                    fin.close();
                } catch (IOException ex) {
                    Log.w(TAG, "openFile failed closing input", ex);
                }
                try {
                    fout.close();
                } catch (IOException ex) {
                    Log.w(TAG, "openFile failed closing output", ex);
                }
            }
        }
    });
}

结果:

  • 适用于ImageView.setImageURI()
  • 不能使用Android默认图库(Intent.ACTION_VIEWsetDataAndType()
  • 适用于ES图像查看器

看来Gallery不喜欢“管道流”。

这是第二次尝试 - 阅读整个文件,demangle,并作为ParcelFileDescriptor.fromData()

    File file = getImageFile(uri);
    byte[] buffer = readFully(file);
    String mimeType = getType(uri);
    byte mask;
    switch (mimeType) {
        case "image/png":
            mask = (byte) (buffer[0] ^ 137);
            break;
        case "image/webp":
            mask = (byte) (buffer[0] ^ 'R');
            break;
        default:
            mask = 0;
            break;
    }
    for (int i = 0; i < 50; i++) buffer[i] ^= mask;
    return (ParcelFileDescriptor) ParcelFileDescriptor.class.getMethod("fromData", byte[].class, String.class).invoke(null, buffer, getImageFile(uri).getName());

结果:

  • ImageView.setImageURI()无法正常合作。
  • 适用于Android默认图库
  • 适用于ES图像查看器

似乎MemoryFile ParcelFileDescriptor.fromData()不时会在ImageView.setImageURI()获取数据之前关闭并处理。

这是第三次尝试 - 将demangled图像写入临时文件:

    // buffer contains readFully and demangled image binary
    try {
        File tmpFile = File.createTempFile("image", getImageExtension(uri));
        OutputStream os = new FileOutputStream(tmpFile);
        try {
            os.write(buffer);
        } finally {
            try {
                os.close();
            } catch (IOException ex2) {
                Log.w(TAG, "openFile(): closing failed", ex2);
            }
        }
        return ParcelFileDescriptor.open(tmpFile, ParcelFileDescriptor.MODE_READ_ONLY);
    } catch (IOException ex2) {
        Log.e(TAG, "openFile(): writing failed", ex2);
        return null;
    }

结果:

  • 适用于ImageView.setImageURI()
  • 适用于Android默认图库
  • 适用于ES图像查看器

但是,我不喜欢这个解决方案,因为很难确定何时可以删除临时文件。

这三种解决方案都存在缺陷,我找不到完美的解决方案。做这些事情的“正确”方式是什么?

0 个答案:

没有答案