小技巧Android知识

Android OKHttp3.2文件上传 + Tomcat 8

2016-05-25  本文已影响7061人  heguorui

版权所有,转载注明。


结构示意图.png

1. Tomcat Servler接收数据

引入依赖jar包

        <!-- http://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        <!-- http://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>

上传文件接收处理的Controller

package com.hgr.web.filesresolver;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "/files")
public class FileUploadController {

    @RequestMapping(value="/upload")
    public @ResponseBody void fileupload(final HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        
        //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
        String savePath = request.getSession().getServletContext().getRealPath("/WEB-INF/upload");
        //上传时生成的临时文件保存目录
        String tempPath = request.getSession().getServletContext().getRealPath("/WEB-INF/temp");
        File tmpFile = new File(tempPath);
        if (!tmpFile.exists()) {
            //创建临时目录
            tmpFile.mkdir();
        }
        
        //消息提示
        String message = "";
        try{
            //使用Apache文件上传组件处理文件上传步骤:
            //1、创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
            factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB
            //设置上传时生成的临时文件的保存目录
            factory.setRepository(tmpFile);
            //2、创建一个文件上传解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //监听文件上传进度
            final DecimalFormat df = new DecimalFormat("#00.0");
            upload.setProgressListener(new ProgressListener(){
                public void update(long pBytesRead, long pContentLength, int arg2) {
                    System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
                    /**
                     * 文件大小为:14608,当前已处理:4096
                                        文件大小为:14608,当前已处理:7367
                                        文件大小为:14608,当前已处理:11419
                                        文件大小为:14608,当前已处理:14608
                     */
                    double percent= (double)pBytesRead*100/(double)pContentLength;  
                    System.out.println(df.format(percent));  
                    request.getSession().setAttribute("UPLOAD_PERCENTAGE", df.format(percent));  
                }
            });
             //解决上传文件名的中文乱码
            upload.setHeaderEncoding("UTF-8"); 
            //3、判断提交上来的数据是否是上传表单的数据
            if(!ServletFileUpload.isMultipartContent(request)){
                //按照传统方式获取数据
                return;
            }
            
            //设置上传单个文件的大小的最大值,目前是设置为1024*1024*1024*2字节,也就是2GB
            long data_unit = 1024;
            
            upload.setFileSizeMax(data_unit*data_unit*data_unit*2);//直接写1024,会出现问题,因为1024是整型,int,-2147483648 至 2147483647之间。
            //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10GB
            upload.setSizeMax(data_unit*data_unit*data_unit*10);
            //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem item : list){
                //如果fileitem中封装的是普通输入项的数据
                if(item.isFormField()){
                    String name = item.getFieldName();
                    //解决普通输入项的数据的中文乱码问题
                    String value = item.getString("UTF-8");
                    //value = new String(value.getBytes("iso8859-1"),"UTF-8");
                    System.out.println(name + "=" + value);
                }else{//如果fileitem中封装的是上传文件
                    //得到上传的文件名称,
                    String filename = item.getName();
                    System.out.println(filename);
                    if(filename==null || filename.trim().equals("")){
                        continue;
                    }
                    //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:  c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
                    //处理获取到的上传文件的文件名的路径部分,只保留文件名部分
                    filename = filename.substring(filename.lastIndexOf("\\")+1);
                    //得到上传文件的扩展名
                    String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
                    //如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
                    System.out.println("上传的文件的扩展名是:"+fileExtName);
                    //获取item中的上传文件的输入流
                    InputStream in = item.getInputStream();
                    //得到文件保存的名称
                    String saveFilename = makeFileName(filename);
                    //得到文件的保存目录
                    String realSavePath = makePath(saveFilename, savePath);
                    //创建一个文件输出流
                    FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);
                    //创建一个缓冲区
                    byte buffer[] = new byte[1024];
                    //判断输入流中的数据是否已经读完的标识
                    int len = 0;
                    //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
                    while((len=in.read(buffer))>0){
                        //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
                        out.write(buffer, 0, len);
                    }
                    //关闭输入流
                    in.close();
                    //关闭输出流
                    out.close();
                    //删除处理文件上传时生成的临时文件
                    //item.delete();
                    message = "文件上传成功!";
                }
            }
        }catch (FileUploadBase.FileSizeLimitExceededException e) {
            e.printStackTrace();
            request.setAttribute("message", "单个文件超出最大值!!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }catch (FileUploadBase.SizeLimitExceededException e) {
            e.printStackTrace();
            request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }catch (Exception e) {
            //message= "文件上传失败!未知错误" + e.toString();
            e.printStackTrace();
            request.getRequestDispatcher("/message.jsp").forward(request, response);
        }
        request.setAttribute("message",message);
        request.getRequestDispatcher("/message.jsp").forward(request, response);
    }

前部分代码中,使用的文件名生成器,避免重复上传时同名文件覆盖:


    /**
        * @Method: makeFileName
        * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
        * @Anthor:孤傲苍狼
        * @param filename 文件的原始名称
        * @return uuid+"_"+文件的原始名称
        */ 
        private String makeFileName(String filename){  //2.jpg
            //为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
            return UUID.randomUUID().toString() + "_" + filename;
        }

前部分代码中,使用的文件分散存储算法,hash计算,使得每个目录中存储的文件hash分散:

        /**
         * 为防止一个目录下面出现太多文件,要使用hash算法打散存储
        * @Method: makePath
        * @Description: 
        * @Anthor:孤傲苍狼
        *
        * @param filename 文件名,要根据文件名生成存储目录
        * @param savePath 文件存储路径
        * @return 新的存储目录
        */ 
        private String makePath(String filename,String savePath){
            //得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
            int hashcode = filename.hashCode();
            int dir1 = hashcode&0xf;  //0--15
            int dir2 = (hashcode&0xf0)>>4;  //0-15
            //构造新的保存目录
            String dir = savePath + "\\" + dir1 + "\\" + dir2;  //upload\2\3  upload\3\5
            //File既可以代表文件也可以代表目录
            File file = new File(dir);
            //如果目录不存在
            if(!file.exists()){
                //创建目录
                file.mkdirs();
            }
            return dir;
        }
}

2. Android Activity

String filepath = "";
String filename = "";

 private static final int FILE_SELECT_CODE = 0;

选择文件,使用Android自带的文件选择器:

    /** 调用文件选择软件来选择文件 **/
    private void showFileChooser() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        try {
            startActivityForResult(Intent.createChooser(intent, "请选择一个要上传的文件"),
                    FILE_SELECT_CODE);
        } catch (android.content.ActivityNotFoundException ex) {
            // Potentially direct the user to the Market with a Dialog
            Toast.makeText(this, "请安装文件管理器", Toast.LENGTH_SHORT)
                    .show();
        }
    }

获取文件选择器返回的文件路径:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case FILE_SELECT_CODE:
                if (resultCode == RESULT_OK) {
                    // Get the Uri of the selected file
                    Uri uri = data.getData();
                    Log.d(TAG, "File Uri: " + uri.toString());

                    // Get the path
                    String path = null;
                    try {
                        path = FileUtils.getPath(this, uri);

                        filename = path.substring(path.lastIndexOf("/") + 1);
                        textView1.setText(path);
                    } catch (URISyntaxException e) {
                        Log.e("TAG", e.toString());
                        //e.printStackTrace();
                        path = "";
                    }
                    filepath = path;
                    Log.d(TAG, "File Path: " + path);
                    // Get the file instance
                    // File file = new File(path);
                    // Initiate the upload
                }
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

使用OKHtpp进行文件上传:
上传时,创建MultipartBody下的Builder对象,在OKHttp2.4.0中为MultipartBuilder,现在改为MultipartBody.Builder,这个需要注意。

//上传文件:
                File file = new File(filepath);
                RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
                RequestBody requestBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addPart(Headers.of(
                                "Content-Disposition",
                                "form-data; name=\"username\""),
                                RequestBody.create(null, "HGR"))
                        .addPart(Headers.of(
                                "Content-Disposition",
                                "form-data; name=\"mFile\"; filename=\"" + filename + "\""), fileBody)
                        .build();
                Request request = new Request.Builder()
                        .url("http://192.168.0.103/NewWebProject/servlet/UploadHandleServlet")
                        .post(requestBody)
                        .build();
                Call call = okHttpClient.newCall(request);
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {

                        Log.e(TAG, "failure upload!");
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {

                        Log.i(TAG, "success upload!");
                    }
                });
//获取文件路径:
package com.guoruihe.okhttptestprj;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;

import java.net.URISyntaxException;

/**
 * Created by guorui.he on 2016/5/23.
 */
public class FileUtils {

    public static String getPath(Context context, Uri uri) throws URISyntaxException {
        if ("content".equalsIgnoreCase(uri.getScheme())) {
            String[] projection = { "_data" };
            Cursor cursor = null;

            try {
                cursor = context.getContentResolver().query(uri, projection, null, null, null);
                int column_index = cursor.getColumnIndexOrThrow("_data");
                if (cursor.moveToFirst()) {
                    return cursor.getString(column_index);
                }
            } catch (Exception e) {
                // Eat it
            }
        }
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }
}

参考:
http://blog.csdn.net/lmj623565791/article/details/47911083 OKHttp文件上传
http://www.cnblogs.com/xdp-gacl/p/4200090.html Java文件上传

上一篇下一篇

猜你喜欢

热点阅读