服务器基于PHP CodeIgniter,Android基于Volley实现多文件/图片上传(含服务器,web版和android客户端完整代码)

问题背景: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,这是个控制器,上传最核心的。

<?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;
    }

}
?>

解释如下:

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

时间: 2024-08-11 03:28:02

服务器基于PHP CodeIgniter,Android基于Volley实现多文件/图片上传(含服务器,web版和android客户端完整代码)的相关文章

Myeclipse中文件已经上传到服务器目录下,文件也没有被占用,但是页面中无法读取和使用问题的解决方法

这个问题是由于Myeclipse中文件不同步引起的.在Myeclipse中,工程文件是由Myeclipse自动扫描添加的,如果在外部修改了工程目录中的文件但又关闭了自动刷新功能,则会引起文件不同步.此外,在外部没有修改Myeclipse工程中的文件也有可能引起该问题. 解决方法: 有两种解决方法: 1)手动刷新.即在Myeclipse的工程目录中,右键refresh(或者按下F5). 2)配置Myeclipse的选项: a)Myeclipse启动时,刷新workspace,即勾选:window-

felayman——PHP中图片上传到服务器

1.upload_file.php <?php //该文件负责获取上传的图片的扩展名和随机生成文件名 header("content-type:text/html;charset=utf-8"); /** * 获取文件扩展名 *Enter description here ... * @param unknown_type $filename */ function getFileName($filename){ //strrchr- 查找指定字符在字符串中的最后一次出现 ret

php form 图片上传至服务器上

本文章也是写给自己看的,因为写的很简洁,连判断都没有,只是直接实现了能上传的功能. 前台: <form action="upload.php" method="POST" enctype="multipart/form-data"> <input type="file" name="myfile" /> <input type="submit" value=

使用canvas给图片添加水印, canvas转换base64,,canvas,图片,base64等转换成二进制文档流的方法,并将合成的图片上传到服务器,

一,前端合成带水印的图片 一般来说,生成带水印的图片由后端生成,但不乏有时候需要前端来处理.当然,前端处理图片一般不建议,一方面js的处理图片的方法不全,二是有些老版本的浏览器对canvas的支持度不够. 下面我们就说说,利用canvas 生成带水印的图片. 1.我们要实现一下效果 2.创建一个canvas var canvas = document.createElement('canvas'); var time = new Date(); var logoCanvas =time+' '+

web操作文件的上传到服务器 并且读取出来

1.文件的上传-servlet实现文件上传---核心API-DiskFileItemFactory 一.文件上传概述 l  实现web开发中的文件上传功能,需完成如下二步操作: ?    在web页面中添加上传输入项 ?    在servlet中读取上传文件的数据,并保存到本地硬盘中. l  如何在web页面中添加上传输入项? ?    <input type="file">标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意: ?    1.必须要设置in

HTML5 文件域+FileReader 分段读取文件并上传到服务器(六)

说明:使用Ajax方式上传,文件不能过大,最好小于三四百兆,因为过多的连续Ajax请求会使后台崩溃,获取InputStream中数据会为空,尤其在Google浏览器测试过程中. 1.简单分段读取文件为Blob,ajax上传到服务器 <div class="container"> <div class="panel panel-default"> <div class="panel-heading">分段读取文件

根目录97 &lt;input file&gt;标签,把图片上传到服务器(跟增删改查一起实现)

1 首先来个简单的html页面: enctype="multipart/form-data" encoding="multipart/form-data" action="../../后台/后台方法.do" //form表单加上这几个属性 action指向后台添加方法 2 3 <input type="button" id="uupdatekeyword2" value="修改"

android选择图片或拍照图片上传到服务器(包括上传参数)

From:http://blog.csdn.net/springsky_/article/details/8213898具体上传代码: 1.选择图片和上传界面,包括上传完成和异常的回调监听 [java] view plaincopy package com.spring.sky.image.upload; import java.util.HashMap; import java.util.Map; import android.app.Activity; import android.app.

Android使用OKHttp库实现视频文件的上传到服务器

目录 1 服务器接口简介 2 Android端代码实现 2.1 xml布局文件 2.2 Activity类 2.3 Okhttp网络通信类 1 服务器接口简介 此处我使用的服务器接口是使用Flask编写,具体实现代码: # -*- coding: utf-8 -*- from flask import Flask, render_template, jsonify, request import time import os import base64 app = Flask(__name__)