(AS笔记)Android选择图片+HttpURLConnection表单POST上传图片到后端

目录

一、前言

二、SpringBoot后端接口

三、Android布局

四、Fragment代码实现

        (1)获取View控件

        (2)请求读写权限,授权再次点击+号打开相册

        (3)相册选择回调图片Uri

五、效果测试 

六、Android网络请求表单提交图片数据

        (1)HttpUtil请求工具类

        (2)开始调用后端接口,上传图片

        (3)执行成功 

七、权限工具类

八、总结


一、前言

        选择图片上传到后端,生成http:域名:端口:/xxx.jpg,将此URL路径存MySQL,Android查询查询数据库获取URL字段数据,使用Glide加载网络图片。


二、SpringBoot后端接口

    @PostMapping("/uploadFile")
    public String uploadFile(@RequestParam(value = "imgFile") MultipartFile imgFile) throws IOException {
        if (imgFile != null && !imgFile.isEmpty()) {
            //获取文件名
            String filename = imgFile.getOriginalFilename(); //图片名
            String[] split = new String[0];
            if (filename != null) {
                split = filename.split("\\.");
            }
            //只接受jpg、png、jpeg格式图片文件,其它格式的文件可按需添加判断,主要是为了防止上传恶意文件,加强安全性
            if ("jpg".equalsIgnoreCase(split[1]) || "png".equalsIgnoreCase(split[1]) || "jpeg".equalsIgnoreCase(split[1])) {
                //图片重命名加后缀
                String photoName = UUID.randomUUID().toString().replace("-", "") + "." + split[1];
                File destFile = new File(logoRealFolderPath + File.separator + photoName);
                //判断是否存在, 不存在就创建
                if (!destFile.getParentFile().exists()) {
                    destFile.getParentFile().mkdirs();
                }
                //压缩图片保存
                Thumbnails.of(imgFile.getInputStream()).scale(0.8).toFile(destFile);
                //获取协议、服务器IP、端口号、工程路径
                String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
                String httpUrl = basePath + uploadReflexPath + photoName;
                System.out.println("完成URL地址 = " + httpUrl);
                //获取到URL后,可以将URL保存到数据库中,以便后续使用,这里就不做演示了,使用Mybatis即可
                if (testService.addUrl(httpUrl) == 0) {
                    return "保存URL到数据库失败";
                } else {
                    return "保存URL到数据库成功,文件地址为:" + httpUrl;
                }
            }
        }
        return "请上传文件后重试";
    }

        说明:api接口:/uploadFile,post的key(表单的name):imgFile


三、Android布局




    

        

        
    

        bg_box_gray.xml



    
    
    
    
    
    

(AS笔记)Android选择图片+HttpURLConnection表单POST上传图片到后端_第1张图片


四、Fragment代码实现

        我是在ViewPager2中Fragment进行布局的,你们可以换成Activity,完全不影响。

        代码的话,我进行步骤讲解,涉及到公司业务代码,这里抱歉,不方便全部粘出来,我会尽量按步骤说清楚。

        (1)获取View控件

//图标选择
private AppCompatImageView ivRegisterPermissionLogo;
private AppCompatTextView tvPermissionShow;

//图标选择
ivRegisterPermissionLogo = baseFindView(R.id.iv_register_permission_logo);
tvPermissionShow = baseFindView(R.id.tv_permission_show);
tvPermissionShow.setText(R.string.add_permission_logo);

        (2)请求读写权限,授权再次点击+号打开相册



 
//SD存储权限组
private final String[] PERMISSIONS = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
//从相册中选择照片Activity的请求码
public static final int CHOOSE_PHOTO = 2;
//回调选中图片到真实路径
private String logoPath = "";
// 多个权限请求Code
private final int REQUEST_CODE_PERMISSIONS = 3;
//图标选择单击事件,触发时获取权限
ivRegisterPermissionLogo.setOnClickListener(v -> requestMorePermissions());
    /**
     * 自定义申请多个权限
     */
    private void requestMorePermissions() {
        PermissionUtils.checkMorePermissions(fragmentContext, PERMISSIONS, new PermissionUtils.PermissionCheckCallBack() {
            @Override
            public void onHasPermission() {
                // 已授予权限,打开相册获取图片真实路径
                Log.i(TAG, "已授予权限");
                choosePermissionLogo();
            }

            @Override
            public void onUserHasAlreadyTurnedDown(String... permission) {
                // 上一次申请权限被拒绝,可用于向用户说明权限原因,然后调用权限申请方法
                Log.i(TAG, "上一次申请权限被拒绝");
                showExplainDialog((dialog, which) -> PermissionUtils.requestMorePermissions(fragmentContext, PERMISSIONS, REQUEST_CODE_PERMISSIONS));
            }

            @Override
            public void onUserHasAlreadyTurnedDownAndDonAsk(String... permission) {
                // 第一次申请权限或被禁止申请权限,建议直接调用申请权限方法。
                Log.i(TAG, "第一次申请权限或被禁止申请权限");
                PermissionUtils.requestMorePermissions(fragmentContext, PERMISSIONS, REQUEST_CODE_PERMISSIONS);
            }
        });
    }

    /**
     * 解释权限的dialog
     */
    private void showExplainDialog(DialogInterface.OnClickListener onClickListener) {
        new AlertDialog.Builder(fragmentContext)
                .setTitle("申请内存读写权限")
                .setMessage("用于打开相册,读取图片路径")
                .setPositiveButton("确定", onClickListener)
                .show();
    }

    /**
     * 打开相册选择权限图标
     */

    private void choosePermissionLogo() {
        //如果重复选择的话,先清空,等新图片路径来重新赋值,如果需要多张路的话,需要使用List来存储多张图片到真实路径
        logoPath = ""; 
        Intent pickIntent = new Intent(Intent.ACTION_PICK, null);
        // 如果限制上传到服务器的图片类型时可以直接写如:"image/jpeg 、 image/png等的类型"
        pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        startActivityForResult(pickIntent, CHOOSE_PHOTO);
    }

    /**
     * 回调申请的权限组
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            PermissionUtils.onRequestMorePermissionsResult(fragmentContext, PERMISSIONS, new PermissionUtils.PermissionCheckCallBack() {
                @Override
                public void onHasPermission() {
                    // 权限已被授予
                    choosePermissionLogo();
                }

                @Override
                public void onUserHasAlreadyTurnedDown(String... permission) {
                    // 拒绝权限
                    Toast.makeText(fragmentContext, "我们需要" + Arrays.toString(permission) + "权限", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onUserHasAlreadyTurnedDownAndDonAsk(String... permission) {
                    //已禁止再次询问权限
                    Toast.makeText(fragmentContext, "我们需要" + Arrays.toString(permission) + "权限", Toast.LENGTH_SHORT).show();
                    showToAppSettingDialog();
                }
            });
        }
    }

        说明:权限请求完整工具类,文末贴出来。

         (3)相册选择回调图片Uri

    //从相册中选择照片Activity的请求码
    public static final int CHOOSE_PHOTO = 2;
    //回调选中图片到真实路径
    private String logoPath = "";

    /**
     * Android 4.4以上打开相册获取图片真实路径
     */
    @TargetApi(19)
    private String handleImageOnKitKat(Intent data) {
        String imagePath = null;
        Uri uri = data.getData();
        Log.i(TAG, "获取的uri=" + uri);
        if (DocumentsContract.isDocumentUri(fragmentContext, uri)) {
            //如果是document类型的Uri ,则通过document_id来处理
            String docId = DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
                String id = docId.split(":")[1];//解析出数据格式的ID
                String selection = MediaStore.Images.Media._ID + "=" + id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
                Log.i(TAG, "类型=media.documents");
            } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId));
                imagePath = getImagePath(contentUri, null);
                Log.i(TAG, "类型=downloads.documents");
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            //如果是普通类型的Uri,则使用普通的方式来处理
            imagePath = getImagePath(uri, null);
            //imagePath = getRealPathFromURI(uri);
            Log.i(TAG, "类型=content");
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            //如果是file类型的uri,直接获取图片路径就可以了
            imagePath = uri.getPath();
            Log.i(TAG, "类型=file");
        }
        Log.i(TAG, "图片真实路径=" + imagePath);
        return imagePath;
    }

    /**
     * Android4.4以下打开相册获取图片真实路径
     */
    private String handleImageBeforeKitKat(Intent data) {
        Uri uri = data.getData();
        String imagePath = getImagePath(uri, null);
        Log.i(TAG, "图片真实路径=" + imagePath);
        return imagePath;
    }

    /**
     * 相册选择回调图片Uri
     */
    @SuppressLint({"ObsoleteSdkInt", "SetTextI18n"})
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO: 从相册获取
        if (requestCode == CHOOSE_PHOTO) {
            if (resultCode == RESULT_OK) {
                if (Build.VERSION.SDK_INT >= 19) {
                    logoPath = handleImageOnKitKat(data);
                } else {
                    logoPath = handleImageBeforeKitKat(data);
                }
            }
        }
        //必须先进行上面两个handleImage方法去解析Uri,将类型转为真实路径的String路径返回给logoPath,然后进行下面的判断操作业务
        if (TextUtils.isEmpty(logoPath) && logoPath.equals("")) {
            tvPermissionShow.setText("请您选择一张权限图标");
            tvPermissionShow.setTextColor(getResources().getColor(R.color.red)); //字体变红色
        } else {
            //不为空,将图片路径转为Bitmap后,使用图片控件显示出来
            ivRegisterPermissionLogo.setImageBitmap(BitmapFactory.decodeFile(logoPath));
            tvPermissionShow.setText("当前图片名:" + logoPath.substring(logoPath.lastIndexOf("/") + 1));
            tvPermissionShow.setTextColor(getResources().getColor(R.color.blue));
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

五、效果测试 

(AS笔记)Android选择图片+HttpURLConnection表单POST上传图片到后端_第2张图片

 

(AS笔记)Android选择图片+HttpURLConnection表单POST上传图片到后端_第3张图片

 


六、Android网络请求表单提交图片数据

        (1)HttpUtil请求工具类

public class HttpUtil {

    private static final String TAG = "HttpUtil";

    //边界参数
    private String boundary;
    //文件上传接口监听
    private requestListener requestListener;

    /**
     * 图片上传
     *
     * @param urlStr         URL
     * @param imgPath        图片路径
     */
    public void upLoadFile(String urlStr, final String imgPath) {
        new Thread(() -> {
            Looper.prepare();
            //自己生一个boundary
            boundary = UUID.randomUUID().toString().replace("-", "");
            HttpURLConnection conn = null;
            try {
                String fileName = "zy" + (int) (Math.random() * 1000000) + ".jpg";
                StringBuilder sb = new StringBuilder();
                //表单数据
                sb.append("--").append(boundary).append("\r\n");
                //图标
                sb.append("Content-Disposition: form-data; name=imgFile" + "; filename=").append(fileName).append("\r\n");
                //传图路径创建文件对象
                File file = new File(imgPath);
                //获取文件名
                String filename = file.getName();
                //截取文件后缀,对应设置mime-type支持类型
                String contentType = "";
                if (filename.endsWith(".png")) {
                    contentType = "image/png";
                }
                if (filename.endsWith(".jpg")) {
                    contentType = "image/jpg";
                }
                if (filename.endsWith(".gif")) {
                    contentType = "image/gif";
                }
                if (filename.endsWith(".bmp")) {
                    contentType = "image/bmp";
                }
                if (contentType.equals("")) {
                    contentType = "application/octet-stream";
                }
                sb.append("Content-Type:").append(contentType).append("\r\n");
                sb.append("\r\n");//此时的sd———>Content-Disposition: form-data; name=imgFile; filename=zy+随机数.jpg
                //字节流处理
                byte[] headerInfo = sb.toString().getBytes(StandardCharsets.UTF_8);
                byte[] endInfo = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);
                conn = (HttpURLConnection) new URL(urlStr).openConnection();
                conn.setDoInput(true);
                conn.setDoOutput(true);
                //文件上传必须为POST
                conn.setRequestMethod("POST");
                //注意这里的格式,模仿表单,出现一点点错误都会导致上传不成功
                conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
                //数据长度
                conn.setRequestProperty("Content-Length", String.valueOf(file.length() + headerInfo.length + endInfo.length));
                //通过conn拿到服务器的字节输出流
                OutputStream out = conn.getOutputStream();
                //需要上传的文件封装成字节输入流
                InputStream in = new FileInputStream(file);
                out.write(headerInfo);
                byte[] buf = new byte[1024];
                int len;
                while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
                out.write(endInfo);
                in.close();
                out.close();
                if (conn.getResponseCode() == 200) {
                    requestListener.success(new BufferedReader(new InputStreamReader(conn.getInputStream())).readLine());
                }
            } catch (Exception e) {
                e.printStackTrace();
                if (Objects.requireNonNull(e.getMessage()).contains("to connect to")) {
                    Log.i(TAG, "服务器访问失败,请联系管理员修复");
                    if (requestListener != null) {
                        requestListener.fail("{ \"code\": 502,\n   \"msg\": \"服务器访问失败,请联系管理员处理\",\n     \"data\":{}}");
                    }
                } else if (Objects.requireNonNull(e.getMessage()).contains("Unable to resolve host")) {
                    Log.i(TAG, "服务器访问失败,请检查网络是否可用");
                    if (requestListener != null) {
                        requestListener.fail("{ \"code\": 503,\n   \"msg\": \"请求失败,请检查网络是否可用\",\n     \"data\":{}}");
                    }
                } else {
                    Log.i(TAG, "服务器访问失败,未知错误:" + e.getMessage());
                    if (requestListener != null) {
                        requestListener.fail("{ \"code\": 504,\n   \"msg\": \"服务器访问失败,错误原因未知,请联系管理员处理\",\n     \"data\":{}}");
                    }
                }
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
            }
            Looper.loop();
        }).start();
    }

    /**
     * 上传监听接口
     *
     * @param requestListener 上传结果回调
     */
    public void setRequestListener(requestListener requestListener) {
        this.requestListener = requestListener;
    }

    /**
     * 定义外部请求结果回调接口
     */
    public interface requestListener {

        /**
         * 成功回调
         *
         * @param resultJson 成功JSON数据
         */
        void success(String resultJson) throws JSONException;

        /**
         * 失败回调
         *
         * @param resultJson 失败JSON数据
         */
        void fail(String resultJson);
    }
}

        (2)开始调用后端接口,上传图片

        HttpUtil uploadFileUtil = new HttpUtil();
        uploadFileUtil.upLoadFile(Constant.ADD_PERMISSION_INFO);
        uploadFileUtil.setRequestListener(new HttpUtil.requestListener() {
            @Override
            public void success(String resultJson) {
                Log.i("zyLogin", "成功: " + resultJson);
           
            }

            @Override
            public void fail(String resultJson) {
                Log.i("zyLogin", "失败: " + resultJson);
                
            }
        });

        (3)执行成功 

(AS笔记)Android选择图片+HttpURLConnection表单POST上传图片到后端_第4张图片

        说明:执行成功后,后端业务代码处理后,会将图片信息存入到云服务器Linux的硬盘内存上,并将Servlet映射路径后外网可访问的URL地址存进数据库,当Android使用Glide就能直接加载图片信息啦。 


七、权限工具类

public class PermissionUtils {

    /**
     * 检测权限
     *
     * @return true:已授权; false:未授权;
     */
    public static boolean checkPermission(Context context, String permission) {
        if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED)
            return true;
        else
            return false;
    }

    /**
     * 检测多个权限
     *
     * @return 未授权的权限
     */
    public static List checkMorePermissions(Context context, String[] permissions) {
        List permissionList = new ArrayList<>();
        for (int i = 0; i < permissions.length; i++) {
            if (!checkPermission(context, permissions[i]))
                permissionList.add(permissions[i]);
        }
        return permissionList;
    }

    /**
     * 请求权限
     */
    public static void requestPermission(Context context, String permission, int requestCode) {
        ActivityCompat.requestPermissions((Activity) context, new String[]{permission}, requestCode);
    }

    /**
     * 请求多个权限
     */
    public static void requestMorePermissions(Context context, List permissionList, int requestCode) {
        String[] permissions = (String[]) permissionList.toArray(new String[permissionList.size()]);
        requestMorePermissions(context, permissions, requestCode);
    }

    /**
     * 请求多个权限
     */
    public static void requestMorePermissions(Context context, String[] permissions, int requestCode) {
        ActivityCompat.requestPermissions((Activity) context, permissions, requestCode);
    }

    /**
     * 判断是否已拒绝过权限
     * 如果应用之前请求过此权限但用户拒绝,此方法将返回 true;
     * 如果应用第一次请求权限或 用户在过去拒绝了权限请求,
     * 并在权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 false。
     */
    public static boolean judgePermission(Context context, String permission) {
        return ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, permission);
    }

    /**
     * 检测权限并请求权限:如果没有权限,则请求权限
     */
    public static void checkAndRequestPermission(Context context, String permission, int requestCode) {
        if (!checkPermission(context, permission)) {
            requestPermission(context, permission, requestCode);
        }
    }

    /**
     * 检测并请求多个权限
     */
    public static void checkAndRequestMorePermissions(Context context, String[] permissions, int requestCode) {
        List permissionList = checkMorePermissions(context, permissions);
        requestMorePermissions(context, permissionList, requestCode);
    }


    /**
     * 检测权限
     * 具体实现由回调接口决定
     */
    public static void checkPermission(Context context, String permission, PermissionCheckCallBack callBack) {
        if (checkPermission(context, permission)) { // 用户已授予权限
            callBack.onHasPermission();
        } else {
            if (judgePermission(context, permission))  // 用户之前已拒绝过权限申请
                callBack.onUserHasAlreadyTurnedDown(permission);
            else                                       // 用户之前已拒绝并勾选了不在询问、用户第一次申请权限。
                callBack.onUserHasAlreadyTurnedDownAndDonAsk(permission);
        }
    }

    /**
     * 检测多个权限
     * 具体实现由回调接口决定
     */
    public static void checkMorePermissions(Context context, String[] permissions, PermissionCheckCallBack callBack) {
        List permissionList = checkMorePermissions(context, permissions);
        if (permissionList.size() == 0) {  // 用户已授予权限
            callBack.onHasPermission();
        } else {
            boolean isFirst = true;
            for (int i = 0; i < permissionList.size(); i++) {
                String permission = permissionList.get(i);
                if (judgePermission(context, permission)) {
                    isFirst = false;
                    break;
                }
            }
            String[] unauthorizedMorePermissions = (String[]) permissionList.toArray(new String[permissionList.size()]);
            if (isFirst)// 用户之前已拒绝过权限申请
                callBack.onUserHasAlreadyTurnedDownAndDonAsk(unauthorizedMorePermissions);
            else       // 用户之前已拒绝并勾选了不在询问、用户第一次申请权限。
                callBack.onUserHasAlreadyTurnedDown(unauthorizedMorePermissions);

        }
    }


    /**
     * 检测并申请权限
     */
    public static void checkAndRequestPermission(Context context, String permission, int requestCode, PermissionRequestSuccessCallBack callBack) {
        if (checkPermission(context, permission)) {// 用户已授予权限
            callBack.onHasPermission();
        } else {
            requestPermission(context, permission, requestCode);
        }
    }

    /**
     * 检测并申请多个权限
     */
    public static void checkAndRequestMorePermissions(Context context, String[] permissions, int requestCode, PermissionRequestSuccessCallBack callBack) {
        List permissionList = checkMorePermissions(context, permissions);
        if (permissionList.size() == 0) {  // 用户已授予权限
            callBack.onHasPermission();
        } else {
            requestMorePermissions(context, permissionList, requestCode);
        }
    }

    /**
     * 判断权限是否申请成功
     */
    public static boolean isPermissionRequestSuccess(int[] grantResults) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            return true;
        else
            return false;
    }

    /**
     * 用户申请权限返回
     */
    public static void onRequestPermissionResult(Context context, String permission, int[] grantResults, PermissionCheckCallBack callback) {
        if (PermissionUtils.isPermissionRequestSuccess(grantResults)) {
            callback.onHasPermission();
        } else {
            if (PermissionUtils.judgePermission(context, permission)) {
                callback.onUserHasAlreadyTurnedDown(permission);
            } else {
                callback.onUserHasAlreadyTurnedDownAndDonAsk(permission);
            }
        }
    }

    /**
     * 用户申请多个权限返回
     */
    public static void onRequestMorePermissionsResult(Context context, String[] permissions, PermissionCheckCallBack callback) {
        boolean isBannedPermission = false;
        List permissionList = checkMorePermissions(context, permissions);
        if (permissionList.size() == 0)
            callback.onHasPermission();
        else {
            for (int i = 0; i < permissionList.size(); i++) {
                if (!judgePermission(context, permissionList.get(i))) {
                    isBannedPermission = true;
                    break;
                }
            }
            // 已禁止再次询问权限
            if (isBannedPermission)
                callback.onUserHasAlreadyTurnedDownAndDonAsk(permissions);
            else // 拒绝权限
                callback.onUserHasAlreadyTurnedDown(permissions);
        }

    }


    /**
     * 跳转到权限设置界面
     */
    public static void toAppSetting(Context context) {
        Intent intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= 9) {
            intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
            intent.setData(Uri.fromParts("package", context.getPackageName(), null));
        } else if (Build.VERSION.SDK_INT <= 8) {
            intent.setAction(Intent.ACTION_VIEW);
            intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
            intent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
        }
        context.startActivity(intent);
    }

    public interface PermissionRequestSuccessCallBack {
        /**
         * 用户已授予权限
         */
        void onHasPermission();
    }


    public interface PermissionCheckCallBack {

        /**
         * 用户已授予权限
         */
        void onHasPermission();

        /**
         * 用户已拒绝过权限
         *
         * @param permission:被拒绝的权限
         */
        void onUserHasAlreadyTurnedDown(String... permission);

        /**
         * 用户已拒绝过并且已勾选不再询问选项、用户第一次申请权限;
         *
         * @param permission:被拒绝的权限
         */
        void onUserHasAlreadyTurnedDownAndDonAsk(String... permission);
    }
}

八、总结

仅自己学习记录,如有错误,敬请谅解~,谢谢~~

你可能感兴趣的:(AS笔记,springboot,Android权限,图片选择,http,Connection)