我想通过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()
。Intent.ACTION_VIEW
和setDataAndType()
)看来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()
无法正常合作。似乎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()
。但是,我不喜欢这个解决方案,因为很难确定何时可以删除临时文件。
这三种解决方案都存在缺陷,我找不到完美的解决方案。做这些事情的“正确”方式是什么?