ImageLoader防盗链

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,大功告成。

发布了37 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/github_34790294/article/details/61407375