超级完整版android 图片上传,包括客户端和后台服务器

想必看到这篇文章你也在网上找了不少的相关博客或代码了,我也是这么过来的,不过我现在已经形成了自己的一套工具

说明:前台用的封装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,到此结束

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,100评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,862评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,993评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,309评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,303评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,421评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,830评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,501评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,689评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,506评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,564评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,286评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,826评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,875评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,114评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,705评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,269评论 2 341

推荐阅读更多精彩内容