文件上传的实现

3 篇文章 0 订阅
2 篇文章 0 订阅

文件上传实现

标签(空格分隔): fileupload ajax php java


概述

文件上传是最古老的互联网操作之一。
最早的HTTP POST是不支持文件上传的,给编程开发带来很多问题。但是在1995年,ietf出台了rfc1867,也就是《RFC 1867 -Form-based File Upload in HTML》,用以支持文件上传。所以Content-Type的类型扩充了multipart/form-data用以支持向服务器发送二进制数据。因此发送post请求时候,表单属性enctype共有二个值可选,这个属性管理的是表单的MIME编码:
① application/x-www-form-urlencoded(默认值)
② multipart/form-data
其实form表单在你不写enctype属性时,也默认为其添加了enctype属性值,默认值是enctype=”application/x- www-form-urlencoded”.

传统形式

ajax出现之前,只能通过最传统的form表单,form表单配置enctype并包含input标签。如下:

<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" >
    <input type="file" id="upload" name="upfile" /> <br />
    <input type="submit" value="upload" name="platform"/>
</form>

展现形式如下:
image_1amq13pdoauu60972u1qo0i1r9.png-3.1kB
点击【Upload】按钮,即可将文件提交至upload.php.

ajax形式

之前文件已经讨论过,参见[论浏览器的局部刷新][2]

传输分析

通过Chrome的开发人员工具可以查看文件上传的网络传输情况,如下图:
image_1amq19cde1c4l1pp3mgce5v1d6nm.png-105kB
浏览器发送了一个POST请求,请求头中的Content-Type、Content-Length尤为重要,如下:

Content-Type:multipart/form-data; boundary=—-WebKitFormBoundary2WqHnU60gxoCkr4C
Content-Length: 288

请求体(body部分)如下:

——WebKitFormBoundary2WqHnU60gxoCkr4C
Content-Disposition: form-data; name=”upfile”; filename=””
Content-Type: application/octet-stream

——WebKitFormBoundary2WqHnU60gxoCkr4C
Content-Disposition: form-data; name=”platform”

upload
——WebKitFormBoundary2WqHnU60gxoCkr4C–

可以看出,content-type的multipart/form-data指示了当前post请求传输的是文件的二进制流,boundary指定了分隔符,body中的各字段直接通过boundary进行分割。在本例中,body中包含upfile、platform两个字段。
服务端收到前端POST请求后,使用特殊的处理逻辑进行处理,下面我们分别介绍使用php和使用java来处理上传逻辑。

PHP处理

php处理文件上传相对来说比较简单,也是我见过处理起来最为简单的一个语言(接触后端语言有限,暂时作此评价)。只需要通过$_FILES即可获取指定的上传文件,代码如下:

<html>
<head></head>
<body>
<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" id="upload" name="upload">
    <input type="submit" value="Upload">
</form>
<?php
if(is_uploaded_file($_FILES['upload']['tmp_name'])){
    $upfile=$_FILES["upload"];
    //获取数组里面的值
    $name=$upfile["name"];//上传文件的文件名
    $type=$upfile["type"];//上传文件的类型
    $size=$upfile["size"];//上传文件的大小
    $tmp_name=$upfile["tmp_name"];//上传文件的临时存放路径
    echo "fileNames -> upload|".$name."|".$size;
    echo "<br />"
}
?>
</body></html>

代码分析

  1. 通过$_FILES获取指定的文件对象;
  2. 通过系统函数is_uploaded_file(file)来判断文件是否是上传文件,确保而已的用户无法欺骗脚本去访问不能访问的文件,例如/etc/passwd;
  3. 直接通过属性name、type、size即可获取文件的属性值。

总结
无需借助任何框架,直接通过系统代码即可实现文件上传的功能,怎一个“赞”字了得。当然,搭建一个完整的服务只靠一个php文件肯定是不行的,具体细节会另外具文讨论。

java处理

springMVC处理

java在servlet3.0之前,处理文件文件上传比较复杂,一般会借助第三方库,下面介绍通过springMVC + FileUpload 来实现文件上传。

package com.shushanfx.uploadspring;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by shushanfx on 2016/7/4.
 */
@Controller
public class UploadController {
    @RequestMapping(value = "/upload.php", method = RequestMethod.POST)
    public void upload(@RequestParam MultipartFile[] upload, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        List<String> fileNames = new LinkedList<String>();
        if(upload!=null){
            for(MultipartFile file : upload){
                String fileName = file.getName();
                if(fileName!=null){
                    fileNames.add(fileName + "|" + file.getOriginalFilename() + "|" + file.getSize());
                    file.transferTo(new File(file.getOriginalFilename()));
                }
            }
            req.setAttribute("fileNames", fileNames);
        }
        upload(req, resp);
    }

    @RequestMapping(value="/upload.php", method = RequestMethod.GET)
    public void upload(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<form id=\"upload-form\" action=\"upload.php\" method=\"post\" enctype=\"multipart/form-data\">\n" +
                "    <input type=\"file\" id=\"upload\" name=\"upload\" />\n" +
                "    <input type=\"submit\" value=\"Upload\" />\n" +
                "</form>");
        if(req.getAttribute("fileNames")!=null){
            out.println("fileNames -> ");
            List<String> list = (List<String>) req.getAttribute("fileNames");
            for(String item : list){
                out.print(item + "&nbsp;");
            }
            out.println("<br />");
        }
        out.println("</body>");
        out.println("</html>");
    }
}

代码分析

  1. 定义一个Controll使用注解的方式;
  2. 分别定义GET请求和POST请求的处理方式;
  3. POST请求通过MutipartFile[]参数获取;
  4. 解析Multipart参数

总结
使用springMVC的方式,java代码只是其中一部分,配置文件是其中一大部分。详细代码另外具文讨论。

servlet3.0原生态处理

在servlet3.0出现之后,事情就变得简单多了,我们可以通过如下代码完成文件上传。

package com.shushanfx.servlet30;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by shushanfx on 2016/7/4.
 */
@WebServlet(name = "uploadServlet", urlPatterns = "/upload.php",
        loadOnStartup = 1, initParams = {
        @WebInitParam(name = "name", value = "shushanfx提醒您<br />")
})
@MultipartConfig(
        location = "upload",  //文件存放路径,指定的目录必须存在,否则会抛异常
        maxFileSize = 8388608,  //最大上传文件大小,经测试应该是字节为单位
        fileSizeThreshold = 819200, //当数据量大于该值时,内容将被写入文件。(specification中的解释的大概意思,不知道是不是指Buffer size),大小也是已字节单位
        maxRequestSize = 8 * 1024 * 1024 * 6 //针对该 multipart/form-data 请求的最大数量,默认值为 -1,表示没有限制。以字节为单位。
)
public class UploadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        ServletConfig config = getServletConfig();
        PrintWriter out = resp.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println(config.getInitParameter("name"));
        out.println("<form id=\"upload-form\" action=\"upload.php\" method=\"post\" enctype=\"multipart/form-data\">\n" +
                "    <input type=\"file\" id=\"upload\" name=\"upload\" />\n" +
                "    <input type=\"submit\" value=\"Upload\" />\n" +
                "</form>");
        if(req.getAttribute("fileNames")!=null){
            out.println("fileNames -> ");
            List<String> list = (List<String>) req.getAttribute("fileNames");
            for(String item : list){
                out.print(item + "&nbsp;");
            }
            out.println("<br />");
        }
        out.println("</body>");
        out.println("</html>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List<String> fileNames = new LinkedList<String>();
        req.setCharacterEncoding("UTF-8");
        Collection<Part> parts = req.getParts();
        //遍历所有的表单内容,将表单中的文件写入上传文件目录
        for (Iterator<Part> iterator = parts.iterator(); iterator.hasNext();) {
            Part part = iterator.next();
            //从Part的content-disposition中提取上传文件的文件名
            String fileName = part.getName();

            if(fileName!=null){
                fileNames.add(fileName + "|" + part.getSubmittedFileName() + "|" + part.getSize());
                part.write(fileName);
            }
        }
        req.setAttribute("fileNames", fileNames);
        //显示上传的文件列表
        doGet(req, resp);
    }
}

代码分析

  1. 使用Servlet3.0的注解@WebService,实现Servlet的自动注册(妈妈再也不用担心你的web.xml配置).
  2. 使用MultipartConfig注解,通知容器在处理POST请求之前解析BODY,分离文件和普通字段;
  3. 通过req.getParts()获取文件上传列表;
  4. 遍历列表,每个Part对象表示一个文件。具体API可以参考Part

总结
很显然,使用servlet3.0文件上传比使用spring上传要方便得多,如果有条件的话,可以考虑使用该方式。具体代码可以参考

参考文献:

  1. 文件上传:http://www.ruanyifeng.com/blog/2012/08/file_upload.html
  2. is_uploaded_file: http://www.w3school.com.cn/php/func_filesystem_is_uploaded_file.asp
  3. Servlet3.0新特性——文件上传接口: http://pisces-java.iteye.com/blog/723125
  4. Servlet3.0新特性详解: https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/
  5. Part: http://tool.oschina.net/uploads/apidocs/javaEE6/javax/servlet/http/Part.html
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值