超級完整版android 圖片上傳,包括客戶端和后臺服務器

想必看到這篇文章你也在網(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é)束

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容