伺服器基於PHP CodeIgniter,Android基於Volley實現多檔案/圖片上傳(含伺服器,web版和android用戶端完整代碼)

來源:互聯網
上載者:User

標籤:

問題背景:app在上傳圖片時,同時傳遞參數,支援傳遞多個圖片。本文中的環境預設已經配好了伺服器的CodeIgniter架構。事實上不使用這個架構也是可以的。

一,伺服器部分

1,在controllers下的helpers建立檔案upload_helper.php


<?php/** * Make multifile array input complaint with CI_Upload.<br> * For use files[ ] input name you must use it method. * * @author porquero * * @example * In Controller<br> * $this->load->helper(‘upload‘);<br> * multifile_array();<br> * foreach ($_FILES as $file => $file_data) {<br> *    $this->upload->do_upload($file); * ... * * @link http://porquero.blogspot.com/2012/05/codeigniter-multifilearray-upload.html */function multifile_array(){    if(count($_FILES) == 0)        return;    $files = array();    $all_files = $_FILES[‘f_file‘][‘name‘];    $i = 0;    foreach ((array)$all_files as $filename) {        $files[++$i][‘name‘] = $filename;        $files[$i][‘type‘] = current($_FILES[‘f_file‘][‘type‘]);        next($_FILES[‘f_file‘][‘type‘]);        $files[$i][‘tmp_name‘] = current($_FILES[‘f_file‘][‘tmp_name‘]);        next($_FILES[‘f_file‘][‘tmp_name‘]);        $files[$i][‘error‘] = current($_FILES[‘f_file‘][‘error‘]);        next($_FILES[‘f_file‘][‘error‘]);        $files[$i][‘size‘] = current($_FILES[‘f_file‘][‘size‘]);        next($_FILES[‘f_file‘][‘size‘]);    }    $_FILES = $files;}

說明:

a.注意裡面的key為‘f_file‘,這就要求app或web在上傳,建表單的時候將此值對應上。

b.該檔案主要是遍曆$_FILES,通過current得到當前file的資訊轉存到數組裡,然後返回。注意轉存後索引是從1開始的。轉存的欄位有name/type/tmp_name/error/size,使用next移動$_FILES[‘f_file‘][‘key‘]的指標。

2.views裡建立upload_form.php,用來在web上類比測試上傳是否成功:

<html><head>    <title>Upload Form</title></head><body><?php echo $error;?><?php $data = array(‘type‘=>‘shop‘, ‘id‘=>‘1‘);?><?php echo form_open_multipart(‘upload/web_upload‘, ‘‘, $data);?><input type="file" name="f_file[]" multiple="multiple" size="20" /><br /><br /><input type="submit" value="upload" /></form></body></html>
注意:

a,這裡使用了CI架構的form_open_multipart建立一個multipart的表單,訪問的是控制器upload裡的web_upload方法,第三個參數$data,用於類比向伺服器傳遞的post請求參數。當然你也可以在下面加幾個<input>.

b,input裡name對應的是f_file[],這個是跟 伺服器那邊統一好的。

c,若要支援多檔案上傳加上multiple="multiple",不加的話一次只能上傳一個檔案。


3,views下建立upload_success.php,顯示上傳成功後的介面。

<html><head>    <title>Upload Form</title></head><body><h3>Your file was successfully uploaded!</h3><ul>    <?php foreach ($upload_data as $item => $value):?>        <li><?php echo $item;?>: <?php echo $value;?></li>    <?php endforeach; ?></ul><p><?php echo anchor(‘upload‘, ‘Upload Another File!‘); ?></p></body></html>

注意:這裡的$upload_data是控制器上傳成功後傳給view的資料。


4,接下來是最關鍵的一個類,在controllers檔案夾下建立Upload.php,這是個控制器,上傳最核心的。

<?phprequire_once APPPATH . ‘controllers/base/BASE_CI_Controller.php‘;class Upload extends BASE_CI_Controller{    private $m_type = ‘‘;    private $m_id = ‘‘;    private $m_path = ‘./application/cache/image‘;    private $m_error = array();    public function __construct()    {        parent::__construct();        $this->load->helper(array(‘form‘, ‘url‘, ‘upload‘));        //$this->load->model(‘picture_model‘);    }    public function index()    {        $this->load->view(‘upload_form‘, array(‘error‘ => ‘ ‘ ));    }    public function app_upload(){        $this->init_argc();        multifile_array();        foreach ($_FILES as $file => $file_data){            $this->do_upload($file);        }        if($this->m_error == NULL || count($this->m_error) == 0){            $this->output->set_output(json_encode(array(‘msg‘=>‘上傳成功‘)));        }else{            $this->output->set_output(json_encode($this->m_error));        }    }    public function do_upload($file)    {        $config[‘upload_path‘]      =  $this->m_path;        $config[‘allowed_types‘]    = ‘gif|jpg|png|jpeg‘;        $config[‘max_size‘]     = 10240;        $config[‘max_width‘]        = 2000;        $config[‘max_height‘]       = 2000;        $config[‘file_name‘] = Util::random_str();        $this->load->library(‘upload‘, $config);        if ( ! $this->upload->do_upload($file)){            $this->on_upload_error($this->upload->display_errors());        }        else{            $upload_data = $this->upload->data();            $this->on_upload_success($upload_data[‘file_name‘]);        }    }    public function do_upload2($file)    {        $config[‘upload_path‘]      =  $this->m_path;        $config[‘allowed_types‘]    = ‘gif|jpg|png|jpeg‘;        $config[‘max_size‘]     = 10240;        $config[‘max_width‘]        = 2000;        $config[‘max_height‘]       = 2000;        $config[‘file_name‘] = Util::random_str();        $this->load->library(‘upload‘, $config);        if ( ! $this->upload->do_upload($file))        {            $error = array(‘error‘ => $this->upload->display_errors());            $this->load->view(‘upload_form‘, $error);        }        else        {            $data = array(‘upload_data‘ => $this->upload->data());            $this->load->view(‘upload_success‘, $data);        }    }    public function web_upload()    {        multifile_array();        foreach ($_FILES as $file => $file_data){            $this->do_upload2($file);        }    }    private function init_argc() {        $this->m_type = $this->getPost(‘type‘);        $this->m_id = $this->getPost(‘id‘);        $this->m_path = $this->getPath($this->m_type);    }    private function getPath($type){        $path = ‘./application/cache/image/shop‘;        if($type == "shop"){            $path =  ‘./application/cache/image/shop‘;        }        return $path;    }    private function on_upload_success($name){        if($this->m_type == ‘shop‘){            //$this->picture_model->add_shop_picture($this->m_id, $this->m_type, $name);        }else if($this->m_type == ‘avatar‘){            //$this->picture_model->add_user_avatar($this->m_id, $this->m_type, $name);        }    }    private function on_upload_error($error){        $this->m_error[‘msg‘] = $error;    }}?>
解釋如下:

a,這裡Upload是繼承的BASE_CI_Controller,也可以換成CI_Controller,在自己的Base_CI_Controller裡封裝了自己項目一些常用的安全校正邏輯;

b,我定義了m_type記錄上傳圖片的類型,m_id是圖片所屬對象的id,m_path為路徑,根據type不同路徑可以做區分。m_error紀錄錯誤。在建構函式裡,注意把幾個helper都load進來。除此外我還寫了個Picture_model用來操作圖片相關的資料庫,如果不需要這個model,可以注釋掉。

c,app_load()是暴露給app用來上傳的,init_argc()初始化post傳來的各種參數。然後就是調multifile_array();之後遍曆上傳。待上傳完畢後根據m_error裡是否為空白,判斷是該顯示什麼訊息給app。在do_upload()裡的Util::random_str()是個很簡單的對時間戳記md5,防止圖片名字一樣:

Util裡的代碼:

    /**     * 產生新的token     * @return string     */    public static function token(){        $curr = Util::time();        return md5($curr);    }    public static function random_str(){        return Util::token();    }

每次上傳成功後都調on_upload_success() on_upload_error()進行更新資料庫等操作。其中on_upload_success()要接收$upload_data[‘file_name‘]),表示上傳成功後的檔案的名字。

d,web_upload是給web上傳圖片用的,通過do_upload2()上傳成功後就載入一個view來顯示上傳後的資訊。PS:保證你對目的檔案夾有可寫入權限。

先用web測試下效果:http://localhost/~yanzi/city52/index.php/upload


二,用戶端:基於Volley的多檔案/圖片上傳類的封裝

這個比較簡單,基於volley封裝的,MultipartRequest.java

package com.android.nomnom.volley;import android.util.Log;import com.android.volley.AuthFailureError;import com.android.volley.NetworkResponse;import com.android.volley.Request;import com.android.volley.Response;import com.android.volley.VolleyLog;import com.android.volley.toolbox.HttpHeaderParser;import org.apache.http.entity.mime.MultipartEntity;import org.apache.http.entity.mime.content.FileBody;import org.apache.http.entity.mime.content.StringBody;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.nio.charset.Charset;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 功能: * * @author yanzi E-mail: [email protected] * @version 建立時間: 2015-08-09 下午4:32 */public class MultipartRequest extends Request<String>{    private MultipartEntity entity = new MultipartEntity();    private  Response.Listener<String> mListener;    private List<File> mFileParts;    private String mFilePartName;    private Map<String, String> mParams;    /**     * 單個檔案+參數 上傳     * @param url     * @param listener     * @param errorListener     * @param filePartName     * @param file     * @param params     */    public MultipartRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener,                            String filePartName, File file, Map<String, String> params){        super(Method.POST, url, errorListener);        mFileParts = new ArrayList<File>();        if(file != null && file.exists()){            mFileParts.add(file);        }else{            VolleyLog.e("MultipartRequest---file not found");        }        mFilePartName = filePartName;        mListener = listener;        mParams = params;        buildMultipartEntity();    }    /**     * 多個檔案+參數上傳     * @param url     * @param listener     * @param errorListener     * @param filePartName     * @param files     * @param params     */    public MultipartRequest(String url,Response.Listener<String> listener,Response.ErrorListener errorListener                            , String filePartName,List<File> files, Map<String, String> params) {        super(Method.POST, url, errorListener);        mFilePartName = filePartName;        mListener = listener;        mFileParts = files;        mParams = params;        buildMultipartEntity();    }    @Override    protected Response<String> parseNetworkResponse(NetworkResponse response) {        String parsed;        try {            parsed = new String(response.data,                    HttpHeaderParser.parseCharset(response.headers));        } catch (UnsupportedEncodingException e) {            parsed = new String(response.data);        }        return Response.success(parsed,                HttpHeaderParser.parseCacheHeaders(response));    }    @Override    protected void deliverResponse(String response) {        mListener.onResponse(response);    }    @Override    public Map<String, String> getHeaders() throws AuthFailureError {        Map<String, String> headers = super.getHeaders();        if (headers == null || headers.equals(Collections.emptyMap())) {            headers = new HashMap<String, String>();        }        return headers;    }    @Override    public String getBodyContentType() {        return entity.getContentType().getValue();    }    @Override    public byte[] getBody() throws AuthFailureError {        ByteArrayOutputStream bos = new ByteArrayOutputStream();        try{            entity.writeTo(bos);        } catch (IOException e) {            VolleyLog.e("IOException writing to ByteArrayOutputStream");        }        return bos.toByteArray();    }    private void buildMultipartEntity() {        if (mFileParts != null && mFileParts.size() > 0) {            for (File file : mFileParts) {                entity.addPart(mFilePartName, new FileBody(file));            }            long l = entity.getContentLength();            Log.i("YanZi-volley", mFileParts.size() + "個,長度:" + l);        }        try {            if (mParams != null && mParams.size() > 0) {                for (Map.Entry<String, String> entry : mParams.entrySet()) {                    entity.addPart(                            entry.getKey(),                            new StringBody(entry.getValue(), Charset                                    .forName("UTF-8")));                }            }        } catch (UnsupportedEncodingException e) {            VolleyLog.e("UnsupportedEncodingException");        }    }}

使用的話new一個request,然後add到queue裡就可以了,我就不寫樣本了。下次介紹android上傳/下載檔案帶進度條的實現。

參考:CodeIgniter http://codeigniter.org.cn/

---------------本文系原創,轉載註明作者yanzi1225627


歡迎大家加入PHP CodeIgniter社區群460132647,備忘yanzi





伺服器基於PHP CodeIgniter,Android基於Volley實現多檔案/圖片上傳(含伺服器,web版和android用戶端完整代碼)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.