想必看到这篇文章你也在网上找了不少的相关博客或代码了,我也是这么过来的,不过我现在已经形成了自己的一套工具
说明:前台用的封装Okhttp上传图片,后台用的springBoot(亲测ssm等框架也可以使用,可能代码会有部分变动,所以后台请酌情借鉴)
图片选择,当然是用第三方框架,好用还方便
第一个是图片选择框架
第二个是图片加载工具
需要引用jitpack的库
具体使用我就不多讲了,gIthub地址--->https://github.com/LuckSiege/PictureSelector
需要注意的是要用到gitbub demo包里的一个工具类
所以建议你把demo项目下载,里面的具体调用方法和注释写的很详细
但我们不要繁杂的写在一个Activity里,所以我们自己封装,上代码
这是调用相册的方法,具体其他api可以自己依照我的方法往下加方法即可
package com.xqb.photography_app.tool;
import android.app.Activity;
import android.util.Log;
import androidx.core.content.ContextCompat;
import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.config.PictureConfig;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import com.luck.picture.lib.listener.OnResultCallbackListener;
import com.luck.picture.lib.style.PictureWindowAnimationStyle;
import com.xqb.photography_app.R;
import java.util.List;
/*
*create by xqb on 2020/9/8
*/
public class MyPictureSelector{
private static MyPictureSelectormyPictureSelector;
private MyPictureSelector() {
}
//实现单例
public static MyPictureSelectorgetInstance() {
if (myPictureSelector ==null) {
synchronized (MyPictureSelector.class) {
if (myPictureSelector ==null) {
return myPictureSelector =new MyPictureSelector();
}
}
}
return myPictureSelector;
}
//启动推出动画,demo包里也有具体说明
private PictureWindowAnimationStylepictureWindowAnimationStyle;
//具体调用方法
public void openAlbum(Activity activity,int maxSelectNum,boolean isOneSelect,boolean isCrop,boolean isCircleCrop,boolean isCropBorder,final OnSelectorResult onSelectorResult){
pictureWindowAnimationStyle =new PictureWindowAnimationStyle();
pictureWindowAnimationStyle.ofAllAnimation(R.anim.picture_anim_up_in, R.anim.picture_anim_down_out);
int selectionMode;
if (isOneSelect){
selectionMode=PictureConfig.SINGLE;
}else{
selectionMode=PictureConfig.MULTIPLE;
}
PictureSelector.create(activity)
.openGallery(PictureMimeType.ofImage())
.imageEngine(GlideEngine.createGlideEngine())//这是需要用到demo包里的一个工具类
.theme(R.style.picture_white_style)//style配置demo包里也有
.maxSelectNum(maxSelectNum)
.selectionMode(selectionMode)//单选 PictureConfig.SINGLE
.isSingleDirectReturn(true)// 单选模式下是否直接返回,PictureConfig.SINGLE模式下有效
.isCamera(false)//是否使用相机
.setPictureWindowAnimationStyle(pictureWindowAnimationStyle)// 自定义相册启动退出动画
.circleDimmedLayer(isCircleCrop)// 是否圆形裁剪
.isEnableCrop(isCrop)// 是否裁剪
.showCropFrame(isCropBorder)// 是否显示裁剪矩形边框 圆形裁剪时建议设为false
.isCompress(true)// 是否压缩
.compressQuality(100)// 图片压缩后输出质量 0~ 100
.synOrAsy(false)//同步true或异步false 压缩 默认同步
// .compressSavePath(getPath())//压缩图片保存地址
.setCropDimmedColor(ContextCompat.getColor(activity, R.color.black80Color))// 设置裁剪背景色值
.withAspectRatio(1, 1)// 裁剪比例 如16:9 3:2 3:4 1:1 可自定义
// .renameCompressFile("icon-" + PrefTool.getString(activity, "userPhone", "") + ".jpg")// 重命名压缩文件名、 如果是多张压缩则内部会自动拼上当前时间戳防止重复
.forResult(new MyResultCallback(onSelectorResult));//自定义回调接口
}
//因为这里把调用方法写出来了,所以在activity里的OnActivityResult方法里是监听不到的
//这是后需要自己实现回调接口
public interface OnSelectorResult{
void onResult(List result);
}
//这是demo里自定义的回调类,我稍作了修改,让它变成具体调用方法的实现回调监听接口
private static class MyResultCallbackimplements OnResultCallbackListener{
private OnSelectorResultonSelectorResult;
public MyResultCallback(OnSelectorResult onSelectorResult) {
super();
this.onSelectorResult=onSelectorResult;
}
@Override
public void onResult(List result) {
onSelectorResult.onResult(result);
}
@Override
public void onCancel() {
Log.d("----->图片选择", "onCancel: 退出了");
}
}
}
调用方法
比起demo里的调用方法整洁了不少,看着舒服,尤其是哪个回调接口的实现,我瞬间有自豪的感觉了,哈哈哈哈
result里就是装的图片信息了,如何获取,demo包里也写的清楚,具体我不多说了
最重要的一部要来了,如何上传,因为图片可选一张可选多张,还需携带用户字符信息,上代码
这里的okhttp也是封装了的,带我的大佬写的,感觉还蛮实用,我在他的基础上改成了图片上传请求
public void doGetForPic(String url,JSONObject object, final OkCallback okCallback) {
Log.i("TAG", "" + object.toString());
final MultipartBody.Builder builder =new MultipartBody.Builder();
try {
builder.addFormDataPart("data",object.getString("data"));
JSONArray array=object.getJSONArray("pic");
for (int i=0;i
JSONObject object1=array.getJSONObject(i);
//文件路径中文转码,否者会报错
// StringBuffer stringBuffer = new StringBuffer();
// for (int j = 0, length = object1.getString("picPath").length(); j < length; j++) {
// char c = object1.getString("picPath").charAt(i);
// if (c <= '\u001f' || c >= '\u007f') {
// stringBuffer.append(String.format("\\u%04x", (int) c));
// } else {
// stringBuffer.append(c);
// }
// }
builder.addFormDataPart("img[]",object1.getString("picName"), RequestBody.create(MediaType.parse("image/jpeg"), new File(object1.getString("picPath"))));
}
}catch (JSONException e) {
e.printStackTrace();
Log.e("------>图片上传异常",e+"");
}
RequestBody requestBody = builder.build();
Request.Builder reqBuilder =new Request.Builder();
Request request =reqBuilder
.post(requestBody)
.url(url)
.build();
final Call call =mOkHttpClien.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
if (okCallback !=null) {
//切换到主线程
mHandler.post(new Runnable() {
@Override
public void run() {
okCallback.onFailure(e);
}
});
}
}
@Override
public void onResponse(Call call, final Response response)throws IOException {
try {
if (response !=null && response.isSuccessful()) {
final String json = response.body().string();
if (okCallback !=null) {
Log.i(TAG, "onResponse: " + Thread.currentThread().getName());
mHandler.post(new Runnable() {
@Override
public void run() {
Log.i(TAG, "onResponse: " + Thread.currentThread().getName());
okCallback.onResponse(json);
}
});
return;
}
}
}catch (IOException e) {
e.printStackTrace();
}
if (okCallback !=null) {
mHandler.post(new Runnable() {
@Override
public void run() {
okCallback.onFailure(new Exception("请求失败"));
}
});
}
}
});
}
是不是有点看不懂,其实很好理解,除了上传的参数解析外,其余的操作就是让请求跑在子线程上,请求完成让它回到当前主UI线程
请求调用
private void uploadUserIcon(List result) {
JSONObject object =new JSONObject();
JSONArray array =new JSONArray();
try {
for (LocalMedia media : result) {
JSONObject object1 =new JSONObject();
object1.put("picName", media.getFileName()).put("picPath", media.getCompressPath());
array.put(object1);
}
object.put("pic", array);
object.put("data", new JSONObject()
.put("userPhone", PrefTool.getString(context, "userPhone", ""))
.put("picType", "icon")
.put("userId", PrefTool.getInt(context, "userId", 0)));
}catch (Exception e) {
e.printStackTrace();
}
OkhttpUtil.getInstance().doGetForPic(IP.requestPicUrl, object, new OkhttpUtil.OkCallback() {
@Override
public void onFailure(Exception e) {
e.printStackTrace();
MyToast.toast(context, ToastTextHelper.okhttpOnFailText);
}
@Override
public void onResponse(String json) {
try {
Log.e(TAG, "onResponse: " + json);
JSONObject responseJson =new JSONObject(json);
if (responseJson.getBoolean("status")) {
PrefTool.setString(context, "userIcon",responseJson.getString("data"));
Picasso.get().load(IP.requestImgUrl + PrefTool.getString(context, "userIcon", "")).transform(new CircleTransform()).into(myInfoIcon);
}else {
MyToast.toast(context, responseJson.getString("message"));
}
}catch (JSONException e) {
e.printStackTrace();
Log.e(TAG, ToastTextHelper.okhttpOnResponseErrText);
}
}
});
}
后台服务器解析
添加依赖包
然后以下这是写在service层具体实现的代码
@Override
public MapuploadUserIcon(HttpServletRequest request) {
MultipartRequest multipartRequest = (MultipartRequest) request;
List list = multipartRequest.getFiles("img[]");
JSONObject requestJson =new JSONObject(request.getParameter("data"));
System.out.println("图片参数"+requestJson);
switch (requestJson.getString("picType")) {
case "icon":
MultipartFile file = list.get(0);
if (file ==null || file.getSize() ==0) {
map.put("status",false);
map.put("message","文件损坏");
}else{
String fileName="icon-"+requestJson.getString("userPhone")+".jpg";
String filePath=iconSavePath+fileName;
try {
file.transferTo(new File(filePath));
UserTb userId=new UserTb();
userId.setUserId(requestJson.getInt("userId"));
userId.setUserIcon("/myInfoImg/"+fileName);
int update =userTbMapper.updateByPrimaryKeySelective(userId);
if (update>0){
userTb=userTbMapper.selectByPrimaryKey(requestJson.getInt("userId"));
map.put("status",true);
map.put("message","图片上传成功");
map.put("data",userTb.getUserIcon());
}else{
map.put("status",false);
map.put("message","图片上传失败");
}
}catch (IOException e) {
e.printStackTrace();
map.put("status",false);
map.put("message","图片上传失败");
}
}
break;
case "photo":
break;
}
return map;
}
基本上就完成了,我因为之前写过一个类似的版本,写起现在这个来就很容易了,所以没有写很多注释,但是代码并不难,所以仔细看,不懂的可以再找我要demo,到时候要的多,我写个demo放github,到此结束