Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 服務器基於PHP CodeIgniter,Android基於Volley實現多文件/圖片上傳(含服務器,web版和android客戶端完整代碼)

服務器基於PHP CodeIgniter,Android基於Volley實現多文件/圖片上傳(含服務器,web版和android客戶端完整代碼)

編輯:關於Android編程

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

一,服務器部分

1,在controllers下的helpers新建文件upload_helper.php

 

 


 * For use files[ ] input name you must use it method.
 *
 * @author porquero
 *
 * @example
 * In Controller

 * $this->load->helper('upload');

 * multifile_array();

 * foreach ($_FILES as $file => $file_data) {

 *    $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上模擬測試上傳是否成功:

 



'shop', 'id'=>'1');?> 

注意:

 

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

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

c,若要支持多文件上傳加上multiple=multiple,不加的話一次只能上傳一個文件。

 

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

 



Your file was successfully uploaded!

  • $value):?>
  • :


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

 

 

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

 

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{

    private MultipartEntity entity = new MultipartEntity();

    private  Response.Listener mListener;

    private List mFileParts;
    private String mFilePartName;
    private Map mParams;

    /**
     * 單個文件+參數 上傳
     * @param url
     * @param listener
     * @param errorListener
     * @param filePartName
     * @param file
     * @param params
     */
    public MultipartRequest(String url, Response.Listener listener, Response.ErrorListener errorListener,
                            String filePartName, File file, Map params){
        super(Method.POST, url, errorListener);
        mFileParts = new ArrayList();
        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 listener,Response.ErrorListener errorListener
                            , String filePartName,List files, Map params) {
        super(Method.POST, url, errorListener);
        mFilePartName = filePartName;
        mListener = listener;
        mFileParts = files;
        mParams = params;
        buildMultipartEntity();
    }

    @Override
    protected Response 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 getHeaders() throws AuthFailureError {
        Map headers = super.getHeaders();

        if (headers == null || headers.equals(Collections.emptyMap())) {
            headers = new HashMap();
        }
        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 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上傳/下載文件帶進度條的實現。

 

 

 


 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved