Android前端RxJava2+Retrofit2;后端SpringMvc实现图片上传

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lupengfei1009/article/details/78143431

前言

因为前端使用的rxjava+retrofit+mvp的架构进行实现,因此考虑着图片上传的功能也直接用rxjava+retrofit去实现,结果在使用过程中,发现始终有问题,图片上不去;测试了几天,尝试更新成rxjava2+retrofit2进行测试,结果成功了;因此这里做一下记录;如果不知道rxjava+retrofit使用的朋友可以参考之前写的博客浅谈Android MVP设计模式(简单结合RxJava+Retrofit)

Android前段实现

资源引入

在build.gradle文件中添加rxjava2+retrofit2相关的引入;我这边是以下相关的配置

    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    compile 'com.squareup.okhttp3:okhttp:3.8.1'
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
    compile "io.reactivex.rxjava2:rxjava:2.1.1"
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

service的编写

    @Multipart
    @POST("/imgs/upload")
    Observable<String> uploadImgs(@Part List<MultipartBody.Part> file, @Query("data") String data);

这里传递的是list,目的是方便传输多张图片,如果只有一张,往list里面塞一个值即可

基于Retrofit2自定义Subscriber

在retrofit中我们常用的Subscriber在retrofit2中竟然不见了,替换他的是Observer,为了减少代码的调整,我们自定义一个Subscriber,代码如下,里面异常处理相关的代码,可以根据自己项目情况酌情调整:

public abstract class Subscriber<T> implements Observer<T> {
    @Override
    public void onError(Throwable e) {
        Throwable throwable = e;
        //获取最根源的异常
        while (throwable.getCause() != null) {
            e = throwable;
            throwable = throwable.getCause();
        }

        NetWorkException ex;
        if (e instanceof HttpException) {             //HTTP错误
            HttpException httpException = (HttpException) e;
            NetWorkCodeEnum netWorkCode = NetWorkCodeEnum.getNetWorkCode(httpException.code() + "");
            if (null != netWorkCode)
                ex = new NetWorkException(netWorkCode.getHttpCode() + "", netWorkCode.getMessage());
            else
                ex = new NetWorkException(NetWorkCodeEnum.UNKNOWN.getHttpCode() + "", NetWorkCodeEnum.UNKNOWN.getMessage());
        } else if (e instanceof ConnectException) {
            ex = new NetWorkException(NetWorkCodeEnum.CODE_503.getHttpCode() + "", NetWorkCodeEnum.CODE_503.getMessage());
        } else if (e instanceof ResultException) {    //服务器返回的错误
            ResultException resultException = (ResultException) e;
            ex = new NetWorkException(resultException.getmErrorCode(), resultException.getMessage());
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            ex = new NetWorkException(NetWorkCodeEnum.CODE_1001.getHttpCode() + "", NetWorkCodeEnum.CODE_1001.getMessage());//均视为解析错误
        } else if (e instanceof NetWorkException) {
            ex = (NetWorkException) e;
        } else {
            if (null != e.getMessage() && e.getMessage().length() > 0) {
                ex = new NetWorkException(NetWorkCodeEnum.OTHER.getHttpCode() + "", e.getMessage());//未知错误
            } else {
                ex = new NetWorkException(NetWorkCodeEnum.UNKNOWN.getHttpCode() + "", NetWorkCodeEnum.UNKNOWN.getMessage());//未知错误
            }
        }
        onError(ex);
    }


    /**
     * 错误回调
     */
    protected abstract void onError(BaseException e);
}

Model层编写

public void uploadImg(List<File> files) {
        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);//表单类型
        for (int i = 0; i < files.size(); i++) {
            File file = files.get(i);
            RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/jpg"), file);
            builder.addFormDataPart("file", file.getName(), photoRequestBody);
        }
        List<MultipartBody.Part> parts = builder.build().parts();

        managerService.uploadImgs(parts, "")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<String>() {

                    @Override
                    public void onSubscribe(@NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@NonNull String s) {

                    }

                    @Override
                    public void onComplete() {

                    }

                    @Override
                    protected void onError(BaseException e) {

                    }
                });
    }

Android调用相机拍照

android调用相机拍照有2种模式,一种是拍照后获取压缩图;一种是拍照之后将图片保存到指定路径,然后根据代码获取原图显示上传

private static int REQUEST_THUMBNAIL = 10;// 请求缩略图信号标识
private static int REQUEST_ORIGINAL = 9;// 请求原图信号标识
  • 第一种方式

    Intent intent1 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // 启动相机
    startActivityForResult(intent1, REQUEST_THUMBNAIL);
  • 第二种方式

    //SD卡路径
    String sdPath = Environment.getExternalStorageDirectory().getPath();
    //照片保存的路径及名称
    String picPath = sdPath + "/" + "test.png";
    
    Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    Uri uri = null;
    if (Build.VERSION.SDK_INT <= 23) {
        //为拍摄的图片指定一个存储的路径
        uri = Uri.fromFile(new File(picPath));
    } else {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(MediaStore.Images.Media.DATA, picPath);
        uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    }
    if (null != uri) {
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(intent2, REQUEST_ORIGINAL);
    }
  • 接受图片

       /**
         * 返回应用时回调方法
         */
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
    
            if (resultCode == RESULT_OK) {
                if (requestCode == REQUEST_THUMBNAIL) {//对应第一种方法
                    /**
                     * 通过这种方法取出的拍摄会默认压缩,因为如果相机的像素比较高拍摄出来的图会比较高清,
                     * 如果图太大会造成内存溢出(OOM),因此此种方法会默认给图片进行压缩
                     */
                    Bundle bundle = data.getExtras();
                    Bitmap bitmap = (Bitmap) bundle.get("data");
                    img.setImageBitmap(bitmap);
    
                    //这里可以再将这个bitmap保存到sd卡中,然后得带一个file对象,即可进行上传;这里就不写了
                } else if (requestCode == REQUEST_ORIGINAL) {//对应第二种方法
                    /**
                     * 这种方法是通过内存卡的路径进行读取图片,所以的到的图片是拍摄的原图
                     */
                    FileInputStream fis = null;
                    try {
                        Log.e("sdPath2", picPath);
                        //把图片转化为字节流
                        fis = new FileInputStream(picPath);
                        //把流转化图片
                        Bitmap bitmap = BitmapFactory.decodeStream(fis);
                        img.setImageBitmap(bitmap);
    
                        //测试上传
                        File file = new File(picPath);
                        List<File> files = new ArrayList<>();
                        //如果有多张如片,通过系统照片选择取到相应的路径,然后添加到list中即可
                        files.add(file);
                        //这里为了方便测试,直接只用了model的代码进行测试了,实际开发的时候,这里需要调用presenter的代码测试
                        uploadImageModel.uploadImg(files);
    
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            fis.close();//关闭流
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

后端实现(SpringMvc)

  • springmvc.xml配置

    <!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 -->
        <bean id="multipartResolver"
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="defaultEncoding" value="UTF-8" />
            <!-- 指定所上传文件的总大小,单位字节。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 20M,可根据情况调整 -->
            <property name="maxUploadSize" value="20971520" />
        </bean>
  • controller层代码编写

        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        @ResponseBody
        public String imageUpload(@RequestParam("file") MultipartFile[] partFiles, String data)
        {
            try
            {
                if (null != partFiles && partFiles.length > 0)
                {
                    for (int i = 0; i < partFiles.length; i++)
                    {
                        MultipartFile partFile = partFiles[i];
                        //服务器图片保存的路径
                        String imgPath = "E:/test/imgs/img" + i + ".png";
                        File imgFile = new File(imgPath);
                        //将图片写到指定的文件下
                        partFile.transferTo(imgFile);
                    }
                    return "{\"status\":0,\"desc\":\"成功\"}";
                }
            }
            catch (Exception e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return "{\"status\":-1,\"desc\":\"失败\"}";
        }

    这里只是写了一个范例,后台如何接受的操作,但是实际开发过程中逻辑相关的代码是应该在service中去写;为了直观的达到效果;这里就直接写在controller里面了。

可能存在的问题

  • 文件大小
    如果说文件太大;可以在stringmvc.xml中将maxUploadSize属性调大一点
  • nginx文件大小
    nginx默认请求最大的文件包为2M,如果上传的图片过大,那么nginx就会报“413 Request Entity Too Large”错误;原因就是因为图片大于2M了,可以在ngnix.conf配置文件中的http{}下添加或者修改client_max_body_size 20m配置,即允许最大的包为20M,大小可根据情况调整;使用nginx -t测试配置是否符合要求;然后通过nginx -s reload重启nginx即可

到这里,单张图片和多张图片即可完成上传了。

猜你喜欢

转载自blog.csdn.net/lupengfei1009/article/details/78143431