您的位置:首页 > 编程语言 > Java开发

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

2017-09-30 15:54 549 查看
前言

Android前段实现
资源引入

service的编写

基于Retrofit2自定义Subscriber

Model层编写

Android调用相机拍照

后端实现SpringMvc
可能存在的问题

前言

因为前端使用的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即可

到这里,单张图片和多张图片即可完成上传了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息