IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Universal-Image-Loader Disk缓存加密功能实现

    grumoon发表于 2015-01-07 10:43:35
    love 0

    Universal-Image-Loader(UIL)这个开源库,相信大家也都熟悉,应该算是Android图片加载这块的集大成者了吧。基本上该有的功能都有了,关于UIL源码的分析,有兴趣的同学可以点击这里。

    UIL将各个功能模块都以接口的形式抽象出来,这样大家可以很方便的进行扩展,同时各个功能模块也都提供了默认的实现,可以满足大部分的需求。优秀的框架的扩展性,真的很赞。

    我们项目中一直使用是UIL,最近项目有个需求,对UIL中在Disk中的缓存进行加密,不希望通过文件管理器能查看这些图片。


    加解密算法不是重点,大家根据自己的需求,选择合适的算法即可(我在项目中选择了简单的AES加密),重点是怎么在UIL中进行拓展。


    采用的方案是:

    1.自定义DiskCache,存入Disk时,将图片文件加密,从Disk取出的时候,将加密后的缓存图片文件解密。

    2.自定义ImageLoadingListener,当图片显示完成后,删除解密后的文件。


    首先是自定义DiskCache

    /**
     * 
     * 实现了加密解密的DiskCache
    * 继承自BaseDiscCache,重点实现了get方法和两个重载的save方法 * * @author grumoon * */ public class SecureDiscCache extends BaseDiscCache { private static final String TEMP_IMAGE_POSTFIX = ".tmp"; private static final String TAG = "SecureDiscCache"; private FileSecUtil fileSecUtil = null; public SecureDiscCache(File cacheDir) { super(cacheDir); this.fileSecUtil = new FileSecUtil(); } public SecureDiscCache(File cacheDir, File reserveCacheDir) { super(cacheDir, reserveCacheDir); this.fileSecUtil = new FileSecUtil(); } public SecureDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) { super(cacheDir, reserveCacheDir, fileNameGenerator); this.fileSecUtil = new FileSecUtil(); } /** * 根据imageUri获取Disk中缓存文件,将其解密并返回。 */ @Override public File get(String imageUri) { Log.v(TAG, "get->" + imageUri); File sourceFile = getFile(imageUri); File imageFile = null; try { // 解密 imageFile = fileSecUtil.decrypt(sourceFile, sourceFile.getAbsolutePath() + FileSecUtil.DECRYPT_TEMP_FILE_TYPE, FileSecUtil.SEC_KEY); } catch (Exception e) { imageFile = null; } return imageFile; } /** * 将图片流imageStream写入到Disk中,并做加密处理。 */ @Override public boolean save(String imageUri, InputStream imageStream, CopyListener listener) throws IOException { Log.v(TAG, "save with stream->" + imageUri); File imageFile = getFile(imageUri); File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX); boolean loaded = false; try { OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize); try { loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize); } finally { IoUtils.closeSilently(os); } } finally { IoUtils.closeSilently(imageStream); if (loaded) { try { // 加密 fileSecUtil.encrypt(tmpFile, imageFile.getAbsolutePath(), FileSecUtil.SEC_KEY); } catch (Exception e) { loaded = false; } } tmpFile.delete(); } Log.v(TAG, "save with stream->" + loaded); return loaded; } /** * 将bitmap对象写入到Disk中,并做加密处理。 */ @Override public boolean save(String imageUri, Bitmap bitmap) throws IOException { Log.v(TAG, "save with bitmap->" + imageUri); File imageFile = getFile(imageUri); File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX); OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize); boolean savedSuccessfully = false; try { savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os); } finally { IoUtils.closeSilently(os); if (savedSuccessfully) { try { // 加密 fileSecUtil.encrypt(tmpFile, imageFile.getAbsolutePath(), FileSecUtil.SEC_KEY); } catch (Exception e) { savedSuccessfully = false; } } tmpFile.delete(); } bitmap.recycle(); Log.v(TAG, "save with bitmap->" + savedSuccessfully); return savedSuccessfully; } /** * 获取加密的文件绝对地址 * * @param imageUri * @return */ public String getFilePath(String imageUri) { return getFile(imageUri).getAbsolutePath(); } }

    继承自BaseDiskCache,BaseDiskCache是个抽象类,已基本实现所有功能

    我们这里重写了两个save方法,实现了文件加密

    重写get方法,实现了文件的解密

    这样在缓存的中文件就是加密状态了。


    然后是自定义ImageLoadingListener

    /**
     * 配合SecureDiscCache完成加解密功能 
    * 用于在显示完图片以后,删除未加密文件 * * @author grumoon * */ public class SecureImageLoadingListener implements ImageLoadingListener { @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { deleteImageFile(imageUri); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { deleteImageFile(imageUri); } @Override public void onLoadingCancelled(String imageUri, View view) { deleteImageFile(imageUri); } public void deleteImageFile(String imageUri) { try { ImageLoader imageLoader = ImageLoader.getInstance(); DiskCache diskCache = imageLoader.getDiskCache(); if (diskCache instanceof SecureDiscCache) { SecureDiscCache sdc = (SecureDiscCache) diskCache; new File(sdc.getFilePath(imageUri) + FileSecUtil.DECRYPT_TEMP_FILE_TYPE).delete(); } } catch (Exception e) { } } }
    这个类的主要目的,实在解密文件后,Disk中存在一个未加密的文件,我们需要在显示出图片或者显示失败后删掉他。

    使用方法:

    初始化过程:


    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
    				.diskCache(new SecureDiscCache(cacheDir,cacheDir,new Md5FileNameGenerator()))
    				...其它配置
    				.build();
    ImageLoader.getInstance().init(config);

    显示图片过程:

    ImageLoader.getInstance().displayImage(UrlUtil.preUrl(uri), imageView,
    				ImageLoaderUtil.getDisplayImageOptions(),
    				new SecureImageLoadingListener());


    缺点和不足:

    1.在调用SecureDiskCache的get方法(解密文件,产生未加密的文件)到 显示图片完成删除图片之前的时间内(时间很短),Disk中会存在未加密的文件,希望能得到改进。

    2.SecureDiskCache中的两个save方法,基本上都是复制了BaseDiskCache中的方法,将流或者bitmap存到disk中,再通过加密工具对文件进行处理。最好是能够直接对流或者bitmap进行处理,直接加密生成文件,免去中间过程和提高安全性。




沪ICP备19023445号-2号
友情链接