oss上传图片

在开发APP软件中,boss突然提出想在软件中添加一个多张照片上传的功能,作为菜鸟的我,琢磨了两天,才弄出来,今天特地贴出来。本篇博客主要介绍的是将本地图片上传到服务器的方法技巧。主要技术点是: 
一、运用第三方可以从相册中选取多张图片。 
二、将图片进行压缩处理。 
三、上传到阿里云OSS。

技术相对简单,主要是将这些技术结合起来的例子并不多,而且阿里云OSS的资料也不是很丰富,开发文档也是很简略,因此趁此机会将我的思路共享出来。

PS:请先配置好应用权限。另外,这是公司项目的一个功能,有些地方只好删除,可能看着不是很完整,有时间我再自己做个Demo共享一下。

一、图片选择

android机型几乎每个品牌都有自己的相册,因此为了兼容性,自定义一个图库是比较好的办法,而且这次开发要求选取多张图片,使用自定义的开来也是最佳方案,当然为了开发的简便性,我直接使用的第三方,如果需要自定义的,可以参考下鸿洋大神的博客。 
Android 超高仿微信图片选择器 图片该这么加载 
不过目前,支持多图选择的第三方控件也是挺多的,比如MultiImageSelector、MultipleImagePick、PhotoPicker、GalleryPick等等,我大致看了下,使用方法是大同小异的。这次开发中,我们使用的是GalleryPick,基本使用方法可以看看github的介绍。GalleryPick地址 
这里几乎就没什么好介绍了,需要的注意的地方,YangcyYe也介绍的很详细。代码:

        ImageConfig imageConfig
                        = new ImageConfig.Builder(
                        // GlideLoader 可用自己用的缓存库
                        new GlideLoader())
                        // 如果在 4.4 以上,则修改状态栏颜色 (默认黑色)
                        .steepToolBarColor(getResources().getColor(R.color.blue))
                        // 标题的背景颜色 (默认黑色)
                        .titleBgColor(getResources().getColor(R.color.blue))
                        // 提交按钮字体的颜色  (默认白色)
                        .titleSubmitTextColor(getResources().getColor(R.color.white))
                        // 标题颜色 (默认白色)
                        .titleTextColor(getResources().getColor(R.color.white))
                        // 开启多选   (默认为多选)  (单选 为 singleSelect)
                        .mutiSelect()
                        .crop()
                        // 开启拍照功能 (默认关闭)
                        .showCamera()
                        // 多选时的最大数量   (默认 9 张)
                        .mutiSelectMaxSize(6)
                        // 已选择的图片路径
                        .pathList(path)
                        // 拍照后存放的图片路径(默认 /temp/picture)
                        .filePath("/ImageSelector/Pictures")
                        .requestCode(REQUEST_CODE)
                        .build();


                ImageSelector.open(OrderDetailActivity.this, imageConfig);   // 开启图片选择器
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

图片选择后是在onActivityResult函数的回调的,和自带的图库是一样的。

二、图片压缩

我们公司的项目主要是上传手机拍的照片,现在的手机像素都是极高的,一不小心就是OOM,更重要的是在天朝流量是“奢侈品”,让用户一不小心就是十几M的流量没了,这也是不行的,所以上传前必须对照片进行压缩优化。

网上相关介绍也是颇多,我直接贴代码,各位看看,应该是挺好理解的。

public class BitmapUtils {

    //压缩图片尺寸
    public static Bitmap compressBySize(String pathName, int targetWidth,
                                 int targetHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;// 不去真的解析图片,只是获取图片的头部信息,包含宽高等;
        Bitmap bitmap = BitmapFactory.decodeFile(pathName, options);

        // 得到图片的宽度、高度;
        float imgWidth = options.outWidth;
        float imgHeight = options.outHeight;

        // 分别计算图片宽度、高度与目标宽度、高度的比例;取大于等于该比例的最小整数;
        int widthRatio = (int) Math.ceil(imgWidth / (float) targetWidth);
        int heightRatio = (int) Math.ceil(imgHeight / (float) targetHeight);
        options.inSampleSize = 1;

        // 如果尺寸接近则不压缩,否则进行比例压缩
        if (widthRatio > 1 || widthRatio > 1) {
            if (widthRatio > heightRatio) {
                options.inSampleSize = widthRatio;
            } else {
                options.inSampleSize = heightRatio;
            }
        }

        //设置好缩放比例后,加载图片进内容;
        options.inJustDecodeBounds = false; // 这里一定要设置false
        bitmap = BitmapFactory.decodeFile(pathName, opts);
        return bitmap;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

一般而言,大小保持在720P左右即可,至少我是这么设置的。压缩的图片,可以通过适配器实现QQ控件图片上传时展现的效果。代码:

    GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
    recycler.setLayoutManager(gridLayoutManager);
    adapter = new Adapter(this, path);
    recycler.setAdapter(adapter);
  • 1
  • 2
  • 3
  • 4

其适配器的完整代码

 public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {

    private Context context;
    private LayoutInflater mLayoutInflater;
    private List<String> result;
    private final static String TAG = "Adapter";

    public Adapter(Context context, List<String> result) {
        mLayoutInflater = LayoutInflater.from(context);
        this.context = context;
        this.result = result;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(mLayoutInflater.inflate(R.layout.image, null));
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Glide.with(context)
                .load(result.get(position))
                .centerCrop()
                .into(holder.image);

    }

    @Override
    public int getItemCount() {
        return result.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView image;

        public ViewHolder(View itemView) {
            super(itemView);
            image = (ImageView) itemView.findViewById(R.id.image);
        }

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

图片如此处理就可以了,但我们公司使用的是阿里云Oss,因此,我把处理过的文件又重新保存到sd里面去了。也把代码贴出来吧:

        long time = System.currentTimeMillis();
        String t = new SimpleDateFormat("yyyy_MM_dd").format(new Date(time));
        String d = new SimpleDateFormat("HH_mm").format(new Date(time));

        // 压缩图片保存的目录路径
        String filePath = MyApplication.getTruename() + "/" + t + "/" + d;

        // 压缩后图片保存的文件名
        String fileName = "photo" + "(" + number + ")" + ".jpg";

        File file = new File(getSDPath() + "/" + "myApp" + "/" + filePath);
        File dir = new File(file, fileName);//将要保存图片的路径,android推荐这种写法,将目录名和文件名分开,不然容易报错。

        try {
            //压缩图片
            Bitmap bitmap = BitmapUtils.compressBySize(srcPath, 1280, 768);
            if (!file.exists()) {
                file.mkdirs();
            }
            if (!dir.exists()) {
                try {
                    dir.createNewFile();
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dir));
                bitmap.compress(Bitmap.CompressFormat.JPEG, 30, bos);
                bos.flush();
                bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

        } catch (Exception e) {
            e.printStackTrace();
        } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

getSdPath就是获取SD的路径,代码如下:

    public static String getSDPath() {
        File sdDir = null;
        boolean sdCardExist = Environment.getExternalStorageState()
                .equals(android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在
        if (sdCardExist) {
            sdDir = Environment.getExternalStorageDirectory();//获取根目录
        } else {
            getBaseContext().getCacheDir().getAbsolutePath(); // 获取内置内存卡目录
        }
        return sdDir.toString();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这样基本就完成了图片处理的操作,现在可以开始进行上传操作了。

三、图片上传

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) {
           // 处理逻辑,requestcode是自己设置和传过来的,RESULT_OK是常量
            path = data.getStringArrayListExtra(ImageSelectorActivity.EXTRA_RESULT);
            // 如果多张图片,可以采取循环上传
            for(String srcPath, path){
                Log.i("MyTag", srcPath) ;  // 可以看到每张原始图片的地址
                // 图片处理和保存的逻辑
                // 图片上传的逻辑,推荐不要放在循环中,多张图片容易造成线程过多
        };
                initOss(); // 配置OSS 
                // 第一张图片的上传
                OssUpload(path.get(0));
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

OSS的配置,建议只实例化一次,即不要放在循环体中。

        // ACCESS_ID,ACCESS_KEY是在阿里云申请的
        OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider(ACCESS_ID, ACCESS_KEY);
        ClientConfiguration conf = new ClientConfiguration();
        conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒
        conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒
        conf.setMaxConcurrentRequest(8); // 最大并发请求数,默认5个
        conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次

        // oss为全局变量,OSS_ENDPOINT是一个OSS区域地址
        oss = new OSSClient(getApplicationContext(), OSS_ENDPOINT, credentialProvider, conf);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

实例化后,就要将图片上传了,这个逻辑应该在onActivityResult这个方法中执行


public void ossUpload(String strPath){

    // 判断图片是否全部上传
    // 如果已经是最后一张图片上传成功,则跳出
    number++;
    if (number == path.size()) {
    // 结束的处理逻辑,并退出该方法

        return;
    }

    // 指定数据类型,没有指定会自动根据后缀名判断
    ObjectMetadata objectMeta = new ObjectMetadata();
    objectMeta.setContentType("image/jpeg");

    // 构造上传请求
    // 这里的objectKey其实就是服务器上的路径,即目录+文件名
    //因为目录命名逻辑涉及公司信息,被我删去,造成不知道这个objectKey不知为何物,如下是我们公司的大致命名逻辑
    //String objectKey = keyPath + "/" + carArr[times] + ".jpg";
    PutObjectRequest put = new PutObjectRequest(BUCKET_NAME, objectKey,dir.getPath());
    put.setMetadata(objectMeta);
    try {
        PutObjectResult putObjectResult = oss.putObject(put);
    } catch (ClientException e) {
        e.printStackTrace();
    } catch (ServiceException e) {
        e.printStackTrace();
    }

    // 异步上传时可以设置进度回调
    put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
        @Override
        public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
            // 在这里可以实现进度条展现功能
            Log.d("PutObject", "currentSize: " + currentSize + " totalSize: " + totalSize);
        }
   });
    OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
        @Override
        public void onSuccess(PutObjectRequest request, PutObjectResult result) {
            Log.d("PutObject", "UploadSuccess");
            Log.d("ETag", result.getETag());
            Log.d("RequestId", result.getRequestId());

            // 这里进行递归单张图片上传,在外面判断是否进行跳出
            if (number <= path.size() - 1) {
                upOss(path.get(number));
            }
        }

        @Override
        public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
            // 请求异常
            if (clientExcepion != null) {
                // 本地异常如网络异常等
                clientExcepion.printStackTrace();
            }
            if (serviceException != null) {
                // 服务异常
                Log.e("ErrorCode", serviceException.getErrorCode());
                Log.e("RequestId", serviceException.getRequestId());
                Log.e("HostId", serviceException.getHostId());
                Log.e("RawMessage", serviceException.getRawMessage());
            }
            ToastUtils.showShort(mContext, "图片上传失败,请重新上传");
            return;
        }
   });
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值