向每一个错误致敬——用户上传破损图片时,你的App会怎样?

问题:在上传图片的时候,如果用户选择了一张破损图片上传,你怎么应对?

凶案现场:

某年某月某日,我正在写代码时,一人拿着手机,怒气冲冲的朝我跑过来,吼道:“怎么回事儿,我上传图片的时候,点这个图片图片就闪退了,点那个图片没事儿。” 我迫不及待的看了下,我靠,还真这么诡异。为什么会这样?

二话不说,插上电脑,打开终端,输入:

 adb logcat *:E

于是,错误尽览无余:

这里写图片描述

很明显在EyishengAPI.java的第600行报了一个NullPointerException

二话不说,追代码,如下:

 599 Bitmap bitmap = BitmapFactory.decodeFile(image1, o2);
 600 bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);

代码的第600行在进行bitmap 的compress.

baos 是啥呢?如下:

ByteArrayOutputStream baos = new ByteArrayOutputStream();

一个字节流,new 了一个ByteArrayOutputStream,这个时候baos肯定不是null,那么……

至此,可以断定,bitmap 为null,BitmapFactory.decodeFile(image1, o2)返回了 null

为什么bitmap为null呢?

  1. 不可能是该图片不存在,如果图片不存在,也就是路径不存在的话,代码是不会走到这里的。
  2. 那么会是什么引起的呢?根据log中的path路径,找到了图库中的该图片,点开后,提示:无法显示该图片缩略图,并且在图库中无该图片的预览。

至此,终于找到了答案,这是一张破损图片。

于是,进行尝试——

  1. 将一个文件 MainActivity.java 重命名为 MainActivity.jpg ,push 到手机上。没错,你敢说,MainActivity.jpg不是一张图片吗?没错,它确实是一张图片,一张破损图片,在Mac上无法查看的图片。
  2. 把 xxxx.mp3 、xxx.mp4 这样的文件重命名为xxxx.jpg or xxx.png , push 到手机上。问题,都会复现。

至此,问题找到了,在上传破损图片时,是无法上传成功。

那么,该怎么办?

在用户上传破损图片时,进行拦截,提示!

问题来了,我在程序中怎么会知道该图片是不是为破损图片呢?

刚才在解析图片时,返回的bitmap为null。那么,是不是,只要是破损图片,解析后返回的bitmap都为null呢?

在进行上面的尝试时,屡试不爽,确实在解析图片时,如果该图片为破损图片,返回的bitmap为null。从这儿得到启发:

在解析时:

  1. 如果返回的bitmap为null,进行拦截,并提示;
  2. 如果返回的bitmap不为null,则放行,走上传图片逻辑
BitmapDecoder decoder = new BitmapDecoder() {
    @Override

    public Bitmap decodeBitmapWithOption(BitmapFactory.Options options) {
        return BitmapFactory.decodeFile(picuri , options);
    }
};
Bitmap  bitmap_temp = decoder.decodeBitmap("width" , "height");

if (bitmap_temp == null){

    L.et("decoder" , "bitmap is null");

    Toast.makeText(getActivity(), "图片解析失败,请检查该图片是否正常", Toast.LENGTH_SHORT).show();

    if (dialog!=null){
        dialog.dismiss();
    }
    return;
}else{
    .....
    //正常逻辑处理
}

用到的BitmapDecoder类:



package com.Util;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory.Options;
import android.util.Log;

/**
 * 封装先加载图片bound,计算出inSmallSize之后再加载图片的逻辑操作
 * 
 */
public abstract class BitmapDecoder {

    /**
     * @param options
     * @return
     */
    public abstract Bitmap decodeBitmapWithOption(Options options);

    /**
     * @param width 图片的目标宽度
     * @param height 图片的目标高度
     * @return
     */
    public Bitmap decodeBitmap(int width, int height) {
        // 如果请求原图,则直接加载原图
        if (width <= 0 || height <= 0) {
            return decodeBitmapWithOption(null);
        }

        // 1、获取只加载Bitmap宽高等数据的Option, 即设置options.inJustDecodeBounds = true;
        Options options = getJustDecodeBoundsOptions();
        // 2、通过options加载bitmap,此时返回的bitmap为空,数据将存储在options中
        decodeBitmapWithOption(options);
        // 3、计算缩放比例, 并且将options.inJustDecodeBounds设置为false;
        calculateInSmall(options, width, height);
        // 4、通过options设置的缩放比例加载图片
        return decodeBitmapWithOption(options);
    }

    /**
     * 加载原图
     * 
     * @return
     */
    public Bitmap decodeOriginBitmap() {
        return decodeBitmapWithOption(null);
    }

    /**
     * @return
     */
    private Options getJustDecodeBoundsOptions() {
        //
        Options options = new Options();
        // 设置为true,表示解析Bitmap对象,该对象不占内存
        options.inJustDecodeBounds = true;
        return options;
    }

    /**
     * @param options
     * @param width
     * @param height
     */
    protected void calculateInSmall(Options options, int width, int height) {
        // 设置缩放比例
        options.inSampleSize = computeInSmallSize(options, width, height);

        Log.d("", "$## inSampleSize = " + options.inSampleSize
                + ", width = " + width + ", height= " + height);
        // 图片质量
        options.inPreferredConfig = Config.RGB_565;

        // 设置为false,解析Bitmap对象加入到内存中
        options.inJustDecodeBounds = false;

        options.inPurgeable = true;
        options.inInputShareable = true;
    }

    private int computeInSmallSize(Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            // Calculate ratios of height and width to requested height and
            // width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value,
            // this will guarantee a final image
            // with both dimensions larger than or equal to the requested
            // height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

            // This offers some additional logic in case the image has a strange
            // aspect ratio. For example, a panorama may have a much larger
            // width than height. In these cases the total pixels might still
            // end up being too large to fit comfortably in memory, so we should
            // be more aggressive with sample down the image (=larger
            // inSampleSize).

            final float totalPixels = width * height;

            // Anything more than 2x the requested pixels we'll sample down
            // further
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
        }
        return inSampleSize;
    }

}

通过这个错误,我深刻的意识到:

  1. 写代码时,自己考虑的还是太少;
  2. 进行图片上传时,要对破损图片进行拦截;
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值