• 从app上传图片到php,再上传到java后端服务器的方法一条龙服务


      在现在的网络开发中,上传图片类的需求实在是太普通不过了,但是对于怎么样做到上传图片,对于刚开始建立项目的时候,还是有点不知所措的。也许有幸,我们做的项目是之前已经有人写过类似的用例了,那么我们只需要依葫芦画瓢就行了。

      好好了解下图片上传(文件上传)的方式,对于认知的提升还是有好处的。而且说不定哪天你就有个这样的需求呢,这里是一条龙上传。

      本文就一个从app到php层,再到java层的流程,演译下整个上传图片的流程吧。

    一、app端获取用户选择的图片,转化为输入流,上传至php前端接口:

    package com.dia.ration;
    
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * 上传文件到服务器类
     */
    public class UploadUtil {
        private static final String TAG = "uploadFile";
        private static final int TIME_OUT = 10 * 1000; // 超时时间
        private static final String CHARSET = "utf-8"; // 设置编码
        /**
         * Android上传文件到服务端
         *
         * @param file 需要上传的文件
         * @param RequestURL 请求的rul
         * @return 返回响应的内容
         */
        public static String uploadFile(File file, String RequestURL) {
            String result = null;
            String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
            String PREFIX = "--", LINE_END = "
    ";
            String CONTENT_TYPE = "multipart/form-data"; // 内容类型
            try {
                URL url = new URL(RequestURL);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setReadTimeout(TIME_OUT);
                conn.setConnectTimeout(TIME_OUT);
                conn.setDoInput(true);          // 允许输入流
                conn.setDoOutput(true);         // 允许输出流
                conn.setUseCaches(false);       // 不允许使用缓存
                conn.setRequestMethod("POST"); // 请求方式
                conn.setRequestProperty("Charset", CHARSET); // 设置编码
                conn.setRequestProperty("connection", "keep-alive");
                conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);
                if (file != null) {
                    DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
                    StringBuffer sb = new StringBuffer();
                    sb.append(PREFIX);
                    sb.append(BOUNDARY);
                    sb.append(LINE_END);
                    /**
                     * 这里重点注意: name里面的值为服务端需要key 只有这个key 才可以得到对应的文件
                     * filename是文件的名字,包含后缀名的 比如:abc.png
                     */
                    sb.append("Content-Disposition: form-data; name="uploadfile"; filename=""
                            + file.getName() + """ + LINE_END);
                    sb.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINE_END);
                    sb.append(LINE_END);
                    dos.write(sb.toString().getBytes());
                    InputStream is = new FileInputStream(file);
                    byte[] bytes = new byte[1024];
                    int len = 0;
                    while ((len = is.read(bytes)) != -1) {
                        dos.write(bytes, 0, len);
                    }
                    is.close();
                    dos.write(LINE_END.getBytes());
                    byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();
                    dos.write(end_data);
                    dos.flush();
                    InputStream input = conn.getInputStream();
                    StringBuffer sb1 = new StringBuffer();
                    int ss;
                    while ((ss = input.read()) != -1) {
                        sb1.append((char) ss);
                    }
                    result = sb1.toString();
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }
        /**
         * 通过拼接的方式构造请求内容,实现参数传输以及文件传输
         *
         * @param url Service net address
         * @param params text content
         * @param files pictures
         * @return String result of Service response
         * @throws IOException
         */
        public static String post(String url, Map<String, String> params, Map<String, File> files)
                throws IOException {
            String BOUNDARY = UUID.randomUUID().toString();
            String PREFIX = "--", LINEND = "
    ";
            String MULTIPART_FROM_DATA = "multipart/form-data";
            String CHARSET = "UTF-8";
            URL uri = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
            conn.setReadTimeout(10 * 1000); // 缓存的最长时间
            conn.setDoInput(true);          // 允许输入
            conn.setDoOutput(true);         // 允许输出
            conn.setUseCaches(false);       // 不允许使用缓存
            conn.setRequestMethod("POST");
            conn.setRequestProperty("connection", "keep-alive");
            conn.setRequestProperty("Charsert", "UTF-8");
            conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);
            // 首先组拼文本类型的参数
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                sb.append(PREFIX);
                sb.append(BOUNDARY);
                sb.append(LINEND);
                sb.append("Content-Disposition: form-data; name="" + entry.getKey() + """ + LINEND);
                sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
                sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
                sb.append(LINEND);
                sb.append(entry.getValue());
                sb.append(LINEND);
            }
            DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
            outStream.write(sb.toString().getBytes());
            // 发送文件数据
            if (files != null)
                for (Map.Entry<String, File> file : files.entrySet()) {
                    StringBuilder sb1 = new StringBuilder();
                    sb1.append(PREFIX);
                    sb1.append(BOUNDARY);
                    sb1.append(LINEND);
                    sb1.append("Content-Disposition: form-data; name="uploadfile"; filename=""
                            + file.getValue().getName() + """ + LINEND);
                    sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND);
                    sb1.append(LINEND);
                    outStream.write(sb1.toString().getBytes());
                    InputStream is = new FileInputStream(file.getValue());
                    byte[] buffer = new byte[1024];
                    int len = 0;
                    while ((len = is.read(buffer)) != -1) {
                        outStream.write(buffer, 0, len);
                    }
                    is.close();
                    outStream.write(LINEND.getBytes());
                }
            byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
            outStream.write(end_data);
            outStream.flush();
            int res = conn.getResponseCode();
            InputStream in = conn.getInputStream();
            StringBuilder sb2 = new StringBuilder();
            if (res == 200) {
                int ch;
                while ((ch = in.read()) != -1) {
                    sb2.append((char) ch);
                }
            }
            outStream.close();
            conn.disconnect();
            return sb2.toString();
        }
        // 测试
        public static void main(String[] args) throws IOException {
            String requestURL = "sss";
            final Map<String, String> params = new HashMap<String, String>();
            params.put("send_userId", String.valueOf(1));
            params.put("send_email", "ss@ss.com");
            final Map<String, File> files = new HashMap<String, File>();
            files.put("uploadfile", new File("/var/data/de.jpg"));
            final String result = UploadUtil.post(requestURL, params, files);
            System.out.println("result is: " + result);
        }
    }

    二、php服务端接收文件,临时保存并继续上传至java后端:

      1. 接收文件类

    <?php
    namespace AppController;
    
    use ActionRestAction;
    use ApiUploadApi;
    
    class UserController extends RestAction
    {
        /**
         * 用户头像上传
         */
        public function set_avatar_post($code)
        {
            $uploadApi = new UploadApi();
            $res = $uploadApi->uploads('avatar');
            $filename = $res['data'];
    
            $result = $uploadApi->uploadAvatar($code, $filename);
            @unlink($filename);            //删除图片
            if (!$result['status']) {
                $this->response($result);
            }
            $avatar = A("Personal", "Api")->getAvatar($code);
            $this->response($avatar);
        }
    }

      2. 上传类

    <?php
    namespace ApiAction;
    
    class UploadApi
    {   
        public function __construct()
        {
            //...
        }
    
        public function curlGet($url, $param = array(), $timeout = 30, $ajaxResponseImmediately = true)
        {
            $opts = array(
                CURLOPT_TIMEOUT => $timeout,
                CURLOPT_RETURNTRANSFER => 1,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false,
                CURLOPT_HTTPHEADER => $header
            );
            switch (strtoupper($method)) {
                // case 'POST':
                    // $opts[CURLOPT_URL] = $url;
                    // $opts[CURLOPT_POST] = 1;
                    // $opts[CURLOPT_POSTFIELDS] = $param;
                    // break;
                default:
                    $opts[CURLOPT_URL] = $url . '?' . http_build_query($param);
                    break;
            }
    
            $ch = curl_init();
            curl_setopt_array($ch, $opts);
            $result = curl_exec($ch);
    
            //记录请求日志
            curl_close($ch);
            return $result;
        }
    
        public function curlPost($url, $param = array(), $timeout = 30, $ajaxResponseImmediately = true)
        {
            $opts = array(
                CURLOPT_TIMEOUT => $timeout,
                CURLOPT_RETURNTRANSFER => 1,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false,
                CURLOPT_HTTPHEADER => $header
            );
            switch (strtoupper($method)) {
                case 'POST':
                default:
                    $opts[CURLOPT_URL] = $url;
                    $opts[CURLOPT_POST] = 1;
                    $opts[CURLOPT_POSTFIELDS] = $param;
                    break;
                    // $opts[CURLOPT_URL] = $url . '?' . http_build_query($param);
                    // break;
            }
    
            $ch = curl_init();
            curl_setopt_array($ch, $opts);
            $result = curl_exec($ch);
    
            $log_data['result'] = $result;
            if (!empty($param)) $log_data['param'] = $param;
            curl_close($ch);
            return $result;
        }
    
        public function uploads($param = '')
        {
            if ($param == '') {
                $param = 'imgFile';
            }
            // 文件保存目录路径
            $save_url = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . "Uploads" . DIRECTORY_SEPARATOR;
            // 定义允许上传的文件扩展名
            $ext_arr = array(
                'image' => array('gif', 'jpg', 'jpeg', 'png', 'bmp'),
            );
            // 最大文件大小
            $max_size = 20 * 1024;
            // PHP上传失败
            if (!empty ($_FILES [$param] ['error'])) {
                switch ($_FILES [$param] ['error']) {
                    case '1' :
                        $error = '超过php.ini允许的大小。';
                        break;
                    case '2' :
                        $error = '超过表单允许的大小。';
                        break;
                    case '3' :
                        $error = '图片只有部分被上传。';
                        break;
                    case '4' :
                        $error = '请选择图片。';
                        break;
                    case '6' :
                        $error = '找不到临时目录。';
                        break;
                    case '7' :
                        $error = '写文件到硬盘出错。';
                        break;
                    case '8' :
                        $error = 'File upload stopped by extension。';
                        break;
                    case '999' :
                    default :
                        $error = '未知错误。';
                }
                $result = array('status' => '0', 'error' => '111111', 'msg' => $error);
    
            }
            // 有上传文件时
            if (empty ($_FILES) === false) {
                $file_name = $_FILES [$param] ['name'];// 原文件名
                $tmp_name = $_FILES [$param] ['tmp_name'];// 服务器上临时文件名
                $file_size = $_FILES [$param] ['size'];// 文件大小
                // 检查文件名
                if (!$file_name) {
                    $result = array('status' => '0', 'error' => '111111', 'msg' => '请选择文件');
                }
                // 检查是否已上传
                if (@is_uploaded_file($tmp_name) === false) {
                    $result = array('status' => '0', 'error' => '111111', 'msg' => '上传失败');
                }
                // 检查文件大小
                if ($file_size > $max_size) {
                    $result = array('status' => '0', 'error' => '111111', 'msg' => '');
                }
                // 检查目录名
                $dir_name = empty ($_GET ['dir']) ? 'image' : trim($_GET ['dir']);
                if (empty ($ext_arr [$dir_name])) {
                    $result = array('status' => '0', 'error' => '111111', 'msg' => '目录名不正确');
                }
                // 获得文件扩展名
                $temp_arr = explode('.', $file_name);
                $file_ext = array_pop($temp_arr);
                $file_ext = trim($file_ext);
                $file_ext = strtolower($file_ext);
                // 检查扩展名
                if (in_array($file_ext, $ext_arr [$dir_name]) === false) {
                    $result = array('status' => '0', 'error' => '111111', 'msg' => '上传文件扩展名是不允许的扩展名');
                }
                // 创建文件夹
                if ($dir_name !== '') {
                    if (!file_exists($save_url)) {
                        mkdir($save_url);
                    }
                }
                $new_file_name = date('YmdHis') . '_' . rand(10000, 99999) . '.' . $file_ext;
                $file_path = $save_url . $new_file_name;
                if (move_uploaded_file($tmp_name, $file_path) === false) {
                    $result['msg'] = '上传文件失败';
                    $result = array('status' => '0', 'error' => '111111', 'msg' => '上传文件失败');
                } else {
                    $result = array('status' => '1', 'error' => '000000', 'data' => $file_path);
                }
                @chmod($file_path, 0644);
                return $result;
            }
        }
    
        public function uploadAvatar($code, $avatarImageName) {
            $url = $this->getApiUrl(__METHOD__);
            $data = array(
                "code" => $code,
                "ip" => $this->params['ip'],
                "avatar" => !empty($avatarImageName) ? '@' . $avatarImageName : '',
            );
            $result = $this->curlPost($url, $data);
            return $result;
        }
    }

      这样,php就已经接收到了来自客户端的 图片上传了,并且已经上传到java后端服务器。

      注意:这里有个坑,即php版本大于5.6以后,直接使用 @ 符号无法上传文件了,需要 加上一个安全选项:CURLOPT_SAFE_UPLOAD => false 才可以,或者使用5.6以的高级上传类上传文件:

    curl_setopt(ch, CURLOPT_POSTFIELDS, [
        'file' => new CURLFile(realpath('image.png')), 
    ]); 

    三、java后端接收php上传的图片

    package com.xx.c.action;
    
    import com.xx.core.pojo.Constants;
    import com.xx.core.pojo.MicroException;
    import com.xx.core.pojo.ResponseEntity;
    import com.xx.core.web.spring.bind.annotation.ClientIP;
    import com.xx.core.web.spring.bind.annotation.SessionUserId;
    import com.xx.c.pojo.user.UpFileUrlBean;
    import com.xx.c.service.user.UserService;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.multipart.MultipartHttpServletRequest;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import java.util.Iterator;
    
    @Controller
    @RequestMapping(value = "upload")
    public class UploadAction {
    
        @Resource(name = "userService")
        private UserService userService;
    
        @RequestMapping(value = "/uploadAvatar", method = RequestMethod.POST, produces = "application/json")
        @ResponseBody
        public Object uploadAvatar(@RequestParam String code, @ClientIP String addIp, @SessionUserId Long userId, 
                @ModelAttribute UpFileUrlBean bean, HttpServletRequest request) throws MicroException {
            bean.setAddIp(addIp);
            bean.setUserId(userId);
            
            try {
                // 转型为MultipartHttpRequest:
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
                // 从其中取出一个文件 后续可使用spring 上传文件方法:file.transferTo(destFile);
                MultipartFile file = null;
                for (Iterator<String> it = multipartRequest.getFileNames(); it.hasNext();) {
                    file = multipartRequest.getFile((String) it.next());
                }
                userService.uploadAvatar(file, bean);
            } catch (Exception e) {
                throw new MicroException(Constants.ErrCode.UPLOAD_AVATAR_TO_SERVER_FAILED, Constants.ErrMsg.UPLOAD_AVATAR_TO_SERVER_FAILED, e);
            }
    
            ResponseEntity ret = new ResponseEntity(Constants.System.OK);
            return ret;
        }
    
    }

      至此,上传流程已经完成了。(当然,后续还可能使用其他上传,比如dubbo调用文件系统上传文件,调用第三方sdk上传到文件服务器。。。, 原理大抵一样,使用字节流进行传输,然后读取出来存储到文件)

      一般为app写的接口中,都会涉及到加解密问题,此时,文件不应该算作加密的范畴,而应单独给一个字段。

  • 相关阅读:
    SparkSql初级编程实践
    云时代架构之苏宁安全架构演进及实践
    云时代架构之知乎网站架构变迁史
    质量属性的六个常见属性场景之《淘宝网》
    云时代架构之游戏服务器的架构演进
    《架构漫谈阅读心得》
    转换后缀表达式
    约瑟夫环(改进3.0)
    栈结构之后缀表达式
    约瑟夫环(改进2.0)
  • 原文地址:https://www.cnblogs.com/yougewe/p/7095884.html
Copyright © 2020-2023  润新知