編輯:關於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上模擬測試上傳是否成功:
a,這裡使用了CI框架的form_open_multipart新建一個multipart的表單,訪問的是控制器upload裡的web_upload方法,第三個參數$data,用於模擬向服務器傳遞的post請求參數。當然你也可以在下面加幾個.
b,input裡name對應的是f_file[],這個是跟 服務器那邊統一好的。
c,若要支持多文件上傳加上multiple=multiple,不加的話一次只能上傳一個文件。
3,views下新建upload_success.php,顯示上傳成功後的界面。
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封裝的,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: yanzi1225627@163.com * @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); } } }
GLSurfaceView渲染過程詳解
GLSurfaceView提供了下列特性:1> 管理一個surface,這個surface就是一塊特殊的內存,能直接排版到android的視圖view上。2>
淺談Android官方MVP架構解讀
綜述對於MVP (Model View Presenter)架構是從著名的MVC(Model View Controller)架構演變而來的。而對於Android應用的開
Android地面站-MavLink解析部分源碼
MavLink是輕量級的通訊協議,主要應用於終端與小型無人載具間的通訊。由於它的通用性,MavLink可以被翻譯成各種語言的代碼應用於各種不同的環境。具體如何通過工具來生
Android開發中的MVC設計模式淺析
Android開發中的MVC設計模式的理解1. Android系統中分層的理解: (1).在Android的軟件開發工作中,應用程序的開發人員主要是應用Android A