想必看到這篇文章你也在網(wǎng)上找了不少的相關(guān)博客或代碼了,我也是這么過來的,不過我現(xiàn)在已經(jīng)形成了自己的一套工具
說明:前臺用的封裝Okhttp上傳圖片,后臺用的springBoot(親測ssm等框架也可以使用,可能代碼會有部分變動,所以后臺請酌情借鑒)
圖片選擇,當然是用第三方框架,好用還方便

第一個是圖片選擇框架
第二個是圖片加載工具

需要引用jitpack的庫
具體使用我就不多講了,gIthub地址--->https://github.com/LuckSiege/PictureSelector
需要注意的是要用到gitbub? demo包里的一個工具類

所以建議你把demo項目下載,里面的具體調(diào)用方法和注釋寫的很詳細
但我們不要繁雜的寫在一個Activity里,所以我們自己封裝,上代碼
這是調(diào)用相冊的方法,具體其他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() {
}
//實現(xiàn)單例
? ? public static MyPictureSelectorgetInstance() {
if (myPictureSelector ==null) {
synchronized (MyPictureSelector.class) {
if (myPictureSelector ==null) {
return myPictureSelector =new MyPictureSelector();
? ? ? ? ? ? ? ? }
}
}
return myPictureSelector;
? ? }
//啟動推出動畫,demo包里也有具體說明
? ? private PictureWindowAnimationStylepictureWindowAnimationStyle;
? ? //具體調(diào)用方法
? ? 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)// 圖片壓縮后輸出質(zhì)量 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")// 重命名壓縮文件名、 如果是多張壓縮則內(nèi)部會自動拼上當前時間戳防止重復
? ? ? ? ? ? ? ? .forResult(new MyResultCallback(onSelectorResult));//自定義回調(diào)接口
? ? }
//因為這里把調(diào)用方法寫出來了,所以在activity里的OnActivityResult方法里是監(jiān)聽不到的
//這是后需要自己實現(xiàn)回調(diào)接口
? ? public interface OnSelectorResult{
void onResult(List result);
? ? }
//這是demo里自定義的回調(diào)類,我稍作了修改,讓它變成具體調(diào)用方法的實現(xiàn)回調(diào)監(jiān)聽接口
? ? 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: 退出了");
? ? ? ? }
}
}
調(diào)用方法
比起demo里的調(diào)用方法整潔了不少,看著舒服,尤其是哪個回調(diào)接口的實現(xiàn),我瞬間有自豪的感覺了,哈哈哈哈

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);
? ? ? ? ? ? ? ? //文件路徑中文轉(zhuǎn)碼,否者會報錯
//? ? ? ? ? ? ? ? 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("請求失敗"));
? ? ? ? ? ? ? ? ? ? ? ? }
});
? ? ? ? ? ? ? ? }
}
});
? ? }
是不是有點看不懂,其實很好理解,除了上傳的參數(shù)解析外,其余的操作就是讓請求跑在子線程上,請求完成讓它回到當前主UI線程
請求調(diào)用
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層具體實現(xiàn)的代碼
@Override
public MapuploadUserIcon(HttpServletRequest request) {
MultipartRequest multipartRequest = (MultipartRequest) request;
? ? List list = multipartRequest.getFiles("img[]");
? ? JSONObject requestJson =new JSONObject(request.getParameter("data"));
? ? System.out.println("圖片參數(shù)"+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;
}
基本上就完成了,我因為之前寫過一個類似的版本,寫起現(xiàn)在這個來就很容易了,所以沒有寫很多注釋,但是代碼并不難,所以仔細看,不懂的可以再找我要demo,到時候要的多,我寫個demo放github,到此結(jié)束