從app上傳圖片到php,再上傳到java後端服務器的方法一條龍服務

  在如今的網絡開發中,上傳圖片類的需求實在是太普通不過了,可是對於怎麼樣作到上傳圖片,對於剛開始創建項目的時候,仍是有點不知所措的。也許有幸,咱們作的項目是以前已經有人寫過相似的用例了,那麼咱們只須要依葫蘆畫瓢就好了。php

  好好了解下圖片上傳(文件上傳)的方式,對於認知的提高仍是有好處的。並且說不定哪天你就有個這樣的需求呢,這裏是一條龍上傳。前端

  本文就一個從app到php層,再到java層的流程,演譯下整個上傳圖片的流程吧。java

1、app端獲取用戶選擇的圖片,轉化爲輸入流,上傳至php前端接口:web

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 = "\r\n";
        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 = "\r\n";
        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);
    }
}

2、php服務端接收文件,臨時保存並繼續上傳至java後端:ajax

  1. 接收文件類spring

<?php
namespace App\Controller;

use Action\RestAction;
use Api\UploadApi;

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. 上傳類json

<?php
namespace Api\Action;

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')), 
]); 

3、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寫的接口中,都會涉及到加解密問題,此時,文件不該該算做加密的範疇,而應單獨給一個字段。

相關文章
相關標籤/搜索