Android使用Okhttp上传单个图片和字符数据到服务器,以及服务器部分实现

最近在写上传图片和参数到后台服务器的功能,网上的各种代码五花八门,大部分使用的还是原生的传输方式,此处我总结了使用Okhttp框架来进行传输的方式。实现了上传单张图片以及字符参数到服务器的功能,本博客还包括服务器部分的实现。

Android端

首先我在自己写的一个HttpUtil工具类中定义了网络请求上传图片到服务器的函数。此函数接收六个参数,如下:

public class HttpUtil {
    /**
     * @param address 服务器的地址
     * @param key 获取图片的key
     * @param filename 图片的文件名称
     * @param file 图片文件
     * @param params 字符参数
     * @param stringCallback 回调接口
     */
    public static void upLoadImageToServer(String address, String key, String filename, File file,
                                     Map<String, String> params, StringCallback stringCallback) {
        OkHttpUtils.post()
                .addFile(key, filename, file)
                .params(params)
                .url(address)
                .build()
                .execute(stringCallback);
    }
}

其中使用到的OkHttpUtils库是鸿洋大神实现的,github地址
我们使用只需在gradle文件中引入:

implementation 'com.zhy:okhttputils:2.6.2'

接着,在我们的具体业务中,使用如下函数,具体去实现上传图片到服务器的整个完整流程:

private void upLoadImageToServer() {
        Map<String, String> params = new HashMap<>();
        params.put("u_id", user.getU_id());
        HttpUtil.upLoadImageToServer(Constant.EDITPHOTO_URL, "u_photo", photoFile.getName(),
                photoFile, params, new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {
                        ImageUtils.deletePhotoFromStorage(user.getU_id());
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(EditPhotoActivity.this, "头像上传失败", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        final BaseMsg msg;
                        try {
                            String responseText = URLDecoder.decode(response, "utf-8");
                            msg = Utility.handleBaseMsgResponse(responseText);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                            return;
                        }

                        if (msg == null) {
                            return;
                        }
                        if (msg.getCode().equals("1")) {
                            String photo = msg.getMsg();
                            user.setU_photo(photo);
                            user.save();
                        } else if (msg.getCode().equals("0")) {
                            ImageUtils.deletePhotoFromStorage(user.getU_id());
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(EditPhotoActivity.this, msg.getMsg(), Toast.LENGTH_SHORT).show();
                                }
                            });
                        }
                    }
                });
    }

可以看到,此处我仅想传递一个u_id的参数到服务器,把这个参数写入一个Map中,然后利用刚才写好的HttpUtil.upLoadImageToServer()函数,向服务器发起网络请求,这里定义了自己的回调接口,用于处理服务器的响应结果。这样,android的部分就成功实现了。

补上代码中用到的ImageUtils类、Utility类和BaseMsg类的实现:

ImageUtils类:

public class ImageUtils {
	// 从sd卡中获取用户头像
    public static Bitmap getPhotoFromStorage(String u_id) {
        String photoPath = Environment.getExternalStorageDirectory() + "/demo/photo/" + u_id + ".jpg";
        return getBitmapFromPath(photoPath, 80, 80);
    }
    
    // 从sd卡中删除用户头像
    public static void deletePhotoFromStorage(String u_id) {
        String photoPath = Environment.getExternalStorageDirectory() + "/demo/photo/";
        File file = new File(photoPath + u_id + ".jpg");
        if (file.exists()) {
            file.delete();
        }
    }
}

Utility类:

public class Utility {
	/**
     * 解析和处理服务器返回的基本消息数据
     */
    public static BaseMsg handleBaseMsgResponse(String response) {
        if (!TextUtils.isEmpty(response)) {
            return new Gson().fromJson(response, BaseMsg.class);
        }
        return null;
    }
}

BaseMsg类:

public class BaseMsg {
    protected String code;
    protected String msg;

    public BaseMsg() {
    }

    public BaseMsg(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "RegistMsg{" +
                "code='" + code + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }
}

服务器端

服务器部分我使用的是ssm框架,参数接收一个request来取出客户端传递的数据。代码每部分的具体功能已在代码注释中给出,可放心食用:

	// 修改头像
    @RequestMapping("/editPhoto")
    public @ResponseBody String editPhoto(HttpServletRequest request) {
        String content; // 返回给客户端的内容

        try {
            request.setCharacterEncoding("utf-8");  //设置编码
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        //创建工厂
        DiskFileItemFactory factory = new DiskFileItemFactory(10*1024*1024, new File("E:/temp"));//设置缓存大小和临时目录
        //得到解析器
        ServletFileUpload sfu = new ServletFileUpload(factory);
        //设置单个文件最大值为10*1024*1024
        sfu.setFileSizeMax(10*1024*1024);

        // 设置保存路径
        String str = request.getServletContext().getRealPath("/");
        int loc = str.indexOf("target");
        String savepath = str.substring(0, loc) + "user_photo";

        //使用sfu去解析request对象,得到List
        try {
            List<FileItem> fileItemList = sfu.parseRequest(new ServletRequestContext(request));
            int photoIndex = -1;
            Map<String, String> map = new HashMap<>();
            for (int i = 0; i < fileItemList.size(); i++) {
                if (fileItemList.get(i).isFormField()) { // 判断是否是普通表单项
                    map.put(fileItemList.get(i).getFieldName(), fileItemList.get(i).getString("utf-8"));
                } else { // 图片类型
                    photoIndex = i;
                }
            }

            // 得到用户id
            String u_id = map.get("u_id");

            //设置图片名称:u_id + 随机16位uuid + 图片后缀
            int begin = fileItemList.get(photoIndex).getName().indexOf(".");
            String suffix = fileItemList.get(1).getName().substring(begin); //截取图片后缀
            String filename = u_id + CommonUtils.uuid(16) + suffix;

            if (!(filename.toLowerCase().endsWith("png") || filename.toLowerCase().endsWith("jpg") || filename.toLowerCase().endsWith("jpeg"))) {
                // 图片格式错误
                content = "{'code':'0', 'msg':'图片格式错误,上传失败'}";
                try {
                    return URLEncoder.encode(content, "utf-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return content;
            }

            //使用目录和文件名称创建目标文件
            File destFile = new File(savepath, filename);
            //保存上传文件到目标文件位置
            fileItemList.get(photoIndex).write(destFile);

            // 删除原头像
            String oldPhoto = userService.getUserInfo(u_id).getU_photo();
            File file = new File(savepath, oldPhoto);
            if (file.exists()) {
                file.delete();
            }

            // 保存到服务器
            userService.savePhoto(u_id, filename);

            // 返回成功信息和图片名给客户端
            content = "{'code':'1', 'msg':'" + filename + "'}";
            try {
                return URLEncoder.encode(content, "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return content;
        } catch (Exception e){
            if(e instanceof FileUploadBase.FileSizeLimitExceededException) {
                // 图片尺寸过大
                content = "{'code':'0', 'msg':'图片尺寸超过10MB,上传失败'}";
                try {
                    return URLEncoder.encode(content, "utf-8");
                } catch (UnsupportedEncodingException e1) {
                    e1.printStackTrace();
                }
                return content;
            }
        }

        return null;
    }

值得注意的是,这里有一个坑,可以看到,我定义了解析工厂,并使用解析器来完成数据的解析。在fileItemList中,如果发现它的size为0,那么说明你在springmvc的配置文件中配置了如下的文件上传的解析器,由于springmvc已经解析过数据了,因此这里二次解析将获取不到数据,只需要将springmvc中的这部分代码注释掉即可:

<bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        
        <property name="maxUploadSize">
            <value>5242880value>
        property>
    bean>

你可能感兴趣的:(Android,Java)