PHP CodeIgniter 基于Volley多文件/图片上传
发布时间:2016-06-22 18:22 | 人气数:2015
问题背景:app在上传图片时,同时传递参数,支持传递多个图片。本文中的环境默认已经配好了服务器的CodeIgniter框架。事实上不使用这个框架也是可以的。
一,服务器部分
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,显示上传成功后的界面。
4,接下来是最关键的一个类,在controllers文件夹下新建Upload.php,这是个控制器,上传最核心的。
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里的代码:
d,web_upload是给web上传图片用的,通过do_upload2()上传成功后就加载一个view来显示上传后的信息。PS:保证你对目的文件夹有可写权限。
先用web测试下效果:http://localhost/~yanzi/city52/index.php/upload
二,客户端:基于Volley的多文件/图片上传类的封装
这个比较简单,基于volley封装的,MultipartRequest.Java
一,服务器部分
1,在controllers下的helpers新建文件upload_helper.PHP
<span style="font-family:Comic Sans MS;font-size:18px;"><?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; }</span>
说明:
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上模拟测试上传是否成功:
<span style="font-family:Comic Sans MS;font-size:18px;"><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></span>注意:
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,显示上传成功后的界面。
<span style="font-family:Comic Sans MS;font-size:18px;"><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></span>注意:这里的$upload_data是控制器上传成功后传给view的数据。
4,接下来是最关键的一个类,在controllers文件夹下新建Upload.php,这是个控制器,上传最核心的。
<span style="font-family:Comic Sans MS;font-size:18px;"><?php require_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; } } ?></span>解释如下:
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里的代码:
<span style="font-family:Comic Sans MS;font-size:18px;"> /** * 产生新的token * @return string */ public static function token(){ $curr = Util::time(); return md5($curr); } public static function random_str(){ return Util::token(); }</span>每次上传成功后都调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
<span style="font-family:Comic Sans MS;font-size:18px;">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<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"); } } } </span>使用的话new一个request,然后add到queue里就可以了,我就不写示例了。
关键词:PHP多文件, codeigniter, volley