ImageLoader防盗链

    xiaoxiao2021-03-25  103

    0.背景

    公司是做和金融相关的,最近领导要整加密,对于图片这种要做防盗链处理,与一般的使用http自带的refer不同的是这次处理是需要向http请求头中添加请求参数,之前的加载图片的框架使用的ImageLoader,但并未听说过有这方面的api(如果有麻烦告诉下,谢谢)。幸好对http还是有简单了解,知道这个的处理无非就是在http请求中添加头或者其他信息。本着这个方向,开始向ImageLoader源码中挖掘(来不及看分析的同学可以直接移步到2.添加防盗链逻辑)。 ##1.寻找网络请求 我们在使用ImageLoader的时候一般是先在Application中进行初始化,然后在配置ImageLoaderConfig:

    public static final DisplayImageOptions imageLoaderBanner = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.loading) .showImageForEmptyUri(R.mipmap.empty) .showImageOnFail(R.mipmap.fail).cacheInMemory(true) .cacheOnDisc(true).considerExifParams(true) .bitmapConfig(Bitmap.Config.RGB_565).build();

    最后,调用:

    ImageLoader.getInstance().displayImage(url, imageView, imageLoaderConfig);

    既然url是从调用的方法传入的,那么我们有理由认为网路请求就在这个方法里。点击进去查看,可以看到如下代码:

    if(bmp != null && !bmp.isRecycled()) { ... } else { if(options.shouldShowImageOnLoading()) { imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources)); } else if(options.isResetViewBeforeLoading()) { imageAware.setImageDrawable((Drawable)null); } imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri)); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options)); if(options.isSyncLoading()) { displayTask.run(); } else { this.engine.submit(displayTask); } }

    上面的if判断走的是缓存,else中是从网络中获取,里面的LoadAndDisplayImageTask就是异步加载图片的任务。点击到LoadAndDisplayImageTask中,查看里面的run方法:

    public void run() { ... Bitmap bmp; label129: { ... bmp = this.tryLoadBitmap(); ... } ... }

    其实这一大段中,我们只关心他的bmp是怎么来的,里面调用了 bmp = this.tryLoadBitmap(),继续查看里面代码:

    private Bitmap tryLoadBitmap() throws LoadAndDisplayImageTask.TaskCancelledException { Bitmap bitmap = null; ... bitmap = this.decodeImage(imageUriForDecoding); ... return bitmap; }

    同样,我们只要知道bitmap的生成,即this.decodeImage(imageUriForDecoding)这个方法,继续查看

    private Bitmap decodeImage(String imageUri) throws IOException { ViewScaleType viewScaleType = this.imageAware.getScaleType(); ImageDecodingInfo decodingInfo = new ImageDecodingInfo(this.memoryCacheKey, imageUri, this.uri, this.targetSize, viewScaleType, this.getDownloader(), this.options); return this.decoder.decode(decodingInfo); }

    这时候调用的是decoder.decode的方法,decoder又是从engine.configuration.decoder来的,这个在ImageLoader中就已经赋值了,是new ImageLoaderEngine(ImageLoaderConfiguration),所以此时的engine就是从ImageLoader中的ImageLoaderConfiguration中得到的。而我们在一般使用时候没有对ImageLoaderConfiguration的decoder赋值,因此在ImageLoaderConfiguration中执行下面逻辑:

    if(this.decoder == null) { this.decoder = DefaultConfigurationFactory.createImageDecoder(this.writeLogs); }

    继续

    public static ImageDecoder createImageDecoder(boolean loggingEnabled) { return new BaseImageDecoder(loggingEnabled); } public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { InputStream imageStream = this.getImageStream(decodingInfo); if(imageStream == null) { L.e("No stream for image [%s]", new Object[]{decodingInfo.getImageKey()}); return null; } else { Bitmap decodedBitmap; BaseImageDecoder.ImageFileInfo imageInfo; try { imageInfo = this.defineImageSizeAndRotation(imageStream, decodingInfo); imageStream = this.resetStream(imageStream, decodingInfo); Options decodingOptions = this.prepareDecodingOptions(imageInfo.imageSize, decodingInfo); decodedBitmap = BitmapFactory.decodeStream(imageStream, (Rect)null, decodingOptions); } finally { IoUtils.closeSilently(imageStream); } if(decodedBitmap == null) { L.e("Image can\'t be decoded [%s]", new Object[]{decodingInfo.getImageKey()}); } else { decodedBitmap = this.considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal); } return decodedBitmap; } }

    其核心代码:

    imageStream = this.resetStream(imageStream, decodingInfo); Options decodingOptions = this.prepareDecodingOptions(imageInfo.imageSize, decodingInfo); decodedBitmap = BitmapFactory.decodeStream(imageStream, (Rect)null, decodingOptions);

    继续查看resetStream这个方法:

    protected InputStream resetStream(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException { try { imageStream.reset(); } catch (IOException var4) { IoUtils.closeSilently(imageStream); imageStream = this.getImageStream(decodingInfo); } return imageStream; }

    继续看getImageStream

    protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException { return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader()); }

    就这样我们找到了ImageDownloader,点进去我们就能看到的http等等,同样由于之前并没有在ImageLoaderConfiguration中进行处理,因此,还是使用默认的:

    if(this.downloader == null) { this.downloader = DefaultConfigurationFactory.createImageDownloader(this.context); }

    因此,我们只需要重写一个类,将防盗链规则加进去,在配置ImageLoaderConfiguration时添加上就OK了。 ##2.添加防盗链逻辑 通过上面的源码跟踪我们可以看到网络请求放在了ImageDownloader这个类中,而这个类我们可以通过ImageLoaderConfiguration设置不同的ImageDownloader,这样我们就可以重写一个ImageDownloader,在其网络请求部分加上我们的防盗链处理:

    import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.Build; import android.webkit.MimeTypeMap; import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class SafeImageDownloader extends BaseImageDownloader { public SafeImageDownloader(Context context) { super(context); } public SafeImageDownloader(Context context, int connectTimeout, int readTimeout) { super(context, connectTimeout, readTimeout); } protected HttpURLConnection createConnection(String url, Object extra) throws IOException { String encodedUrl = Uri.encode(url, "@#&=*+-_.,:!?()/~\'%"); HttpURLConnection conn = (HttpURLConnection) (new URL(encodedUrl)).openConnection(); conn.setConnectTimeout(this.connectTimeout); conn.setReadTimeout(this.readTimeout); conn.setRequestMethod("GET"); conn.setRequestProperty("User-Agent", "FullCat"); return conn; } protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException { String filePath = Scheme.FILE.crop(imageUri); if (this.isVideoFileUri(imageUri)) { return this.getVideoThumbnailStream(filePath); } else { BufferedInputStream imageStream = new BufferedInputStream(new FileInputStream(filePath), '耀'); return new ContentLengthInputStream(imageStream, (int) (new File(filePath)).length()); } } private boolean isVideoFileUri(String uri) { String extension = MimeTypeMap.getFileExtensionFromUrl(uri); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); return mimeType != null && mimeType.startsWith("video/"); } @TargetApi(8) private InputStream getVideoThumbnailStream(String filePath) { if (Build.VERSION.SDK_INT >= 8) { Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(filePath, 2); if (bitmap != null) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos); return new ByteArrayInputStream(bos.toByteArray()); } } return null; } }

    上面的conn.setRequestProperty(“User-Agent”, “FullCat”);就是这个项目中的防盗链处理,同样我们也可以在这个位置添加请求参数,设置header等等。接着,我们需要把这个ImageDownloader设置到ImageLoaderConfiguration中:

    public static void displayImage(String uri, ImageView imageView, DisplayImageOptions options) { ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(imageView.getContext()) .imageDownloader(new SafeImageDownloader(imageView.getContext())) .build(); ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.destroy(); imageLoader.init(configuration); imageLoader.displayImage(uri, imageView, options); }

    OK,大功告成。

    转载请注明原文地址: https://ju.6miu.com/read-26448.html

    最新回复(0)