用commons-fileupload实现文件上传

Form表单的要求

1.提供from表单,method必须是post(post方式可以提交大量数据)

2.提供input type=”file”标签

3.form表单的enctype属性必须是multipart/form-data(就是在设置请求消息头的Content-Type)

  • application/x-www-form-urlencoded(默认)。请求报文内容的格式为name=admin&password=123。服务器通过request.getParameter(“name”);来获取数据
  • multipart/form-data。请求报文内容的格式为
Content-Type: multipart/form-data; boundary=---------------------------28512195225210
Content-Length: 295

-----------------------------28512195225210
Content-Disposition: form-data; name="a"

q
-----------------------------28512195225210
Content-Disposition: form-data; name="b"; filename="a.txt"
Content-Type: text/plain

eaglezsx
-----------------------------28512195225210--

数据被分隔线分成一段一段的,其中每两个分隔线之间的数据称为一个item。request.getParameter(“name”)方法获取指定的表单字段字符内容,但文件上传表单已经不再是字符内容,而是字节内容,所以失效。用request.getInputStream();方法可以获取字节流。

例子

新建一个a.txt,里边随便写点儿东西,eaglezsx。

新建一个upload.jsp

<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/servlet/uploadServlet" method="post">
        <input type="text" name="a" />
        <input type="file" name="b"/></br>
        <input type="submit" value="提交"/>
</form>

新建一个Servlet,UploadServlet.java

public class UploadServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        ServletInputStream sis=request.getInputStream();//获取请求报文中的内容
        int len=-1;
        byte[] b=new byte[1024];
        while ((len=sis.read(b))!=-1) {
            System.out.println(new String(b,0,len));//将请求报文中的内容转化为字符串,打印到控制台
        }
        sis.close();

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

运行结果为

-----------------------------17157105472171
Content-Disposition: form-data; name="a"

hello
-----------------------------17157105472171
Content-Disposition: form-data; name="b"; filename="a.txt"
Content-Type: text/plain

eaglezsx
-----------------------------17157105472171--

这就是从浏览器接收到的那些东西。把这些东西解析了,就可以获得需要的内容了。自己解析起来比较麻烦。有现成的组件commons fileupload来解析这些请求。Apache的官网有它的用户指南http://commons.apache.org/proper/commons-fileupload/using.html里边有个小例子。

在使用FileUpload组件时,需要导入commons-fileupload和commons-io两个jar包。

通过这个例子,可以看出主要用这几样东西

DiskFileItemFactory类

如果上传的文件比较小,将保存在内存中。如果上传的文件比较大,则会以临时文件的形式保存在磁盘的临时文件夹中。默认情况下,文件保存在内存还是硬盘临时文件夹的临界值是10240,即10K。

构造方法

  • DiskFileItemFactory():采用默认临界值10240,默认临时文件夹为D:\Java\apache-tomcat-7.0.52\temp,Tomcat安装目录中的temp文件夹中。
  • DiskFileItemFactory(int sizeThreshold,File repository):采用参数指定临界值和临时文件的存储位置

方法

  • setSizeThreshold(int sizeThreshold):设置临界值
  • setRepository(File repository):设置临时文件的存储位置

ServletFileUpload类

构造方法:

  • ServletFileUpload(FileItemFactory fileItemFactory)

方法:

  • parseRequest(HttpServletRequest req):解析出Form表单中的每个字段的数据,并将它们分别包装成独立的FileItem对象,然后将这些FileItem对象加入进一个List类型的集合对象中返回。
  • isMultipartContent():是个静态方法,判断请求消息中的内容是否是multipart/form-data类型,如果是则返回true。

FileItem接口

  • boolean isFormFiled():用于判断FileItem类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通表单字段则返回true。
  • String getFiledName():获取表单字段name属性的值。
  • String getName():获取文件上传字段中的文件名
  • String getString():获取普通字段的value属性的值。
  • String getString(String encoding):可以设置编码,这样普通标签的value为中文时就不会乱码了
  • void write(File file):将FileItem对象中保存的主体内容保存到某个指定的文件中。如果内容是在临时文件中,那么DiskFileItem被垃圾回收后,临时文件会被自动删除。

  • String getContentType():获取上传文件的类型,即表单字段描述头属性Content-Type的值,如“image/jpeg”。

  • boolean isInMemory():判断FileItem对象封装的数据内容是存储在内存中,还是存储在临时文件中,在内存中则返回true。
  • long getSize():返回该上传文件的大小(以字节为单位)
  • InputStream getInputStream():以流的形式返回上传文件的内容
public class UploadServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        if (ServletFileUpload.isMultipartContent(request)) {

            File repository=new File("G:/");
            DiskFileItemFactory factory=new DiskFileItemFactory(1024*1024, repository);

            ServletFileUpload upload=new ServletFileUpload(factory);

            try {
                List<FileItem> items=upload.parseRequest(request);//这句话执行就会开始产生临时文件,这句话执行完后,临时文件也就产生了,临时文件和上传的文件一样大,后面的操作都是对临时文件的操作
                for (FileItem item : items) {
                    if (item.isFormField()) {
                        processFormField(item);
                    }else {
                        try {
                            processUploadedFile(item);
                        } catch (Exception e) {

                            e.printStackTrace();
                        }
                    }
                }


            } catch (FileUploadException e) {

                e.printStackTrace();
            }

        }else {
            System.out.println("这不是文件上传表单");
        }

    }

    private void processUploadedFile(FileItem item) throws Exception {
        String fieldName=item.getFieldName();
        String fileName=item.getName();
        String contentType=item.getContentType();
        boolean isInMemory=item.isInMemory();
        long sizeInBytes=item.getSize();

        System.out.println(fieldName+" "+fileName+" "+contentType+" "+" "+isInMemory+" "+sizeInBytes);

        //把上传的文件保存d盘
        File uploadedFile=new File("D:/"+fileName);

        item.write(uploadedFile);//这种方式临时文件会自动删除

        //或者采用流的形式操作数据,这种方式需要调用delete方法来删除临时文件
        /*
        InputStream uploadedStream=item.getInputStream();
        FileOutputStream fos=new FileOutputStream(uploadedFile);
        int len=0;
        byte[] b=new byte[1024];
        while((len=uploadedStream.read(b))!=-1){
            fos.write(b,0,len);
        }
        uploadedStream.close();
        fos.close();
        item.delete();
        */
    }

    private void processFormField(FileItem item) {
        String name=item.getFieldName();
        String value=item.getString();

        System.out.println("name:"+name+"---"+"value:"+value);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

显示结果为

name:a---value:hello
b a.txt text/plain  true 8

需要注意的问题

1.getName()在有的浏览器中获取的是文件名(a.txt),但在有些浏览器中获取的就是完整的路径(C:\Users\YZ\Desktop\a.txt)。

解决方法

String filename=fileitem.getName();
filename=filename.substring(filename.lastIndexOf(File.separator)+1);

针对这个问题官网也给出了解决办法http://commons.apache.org/proper/commons-fileupload/faq.html

filename=FilenameUtils.getName(filename);

2.如果客户端上传了一个jsp,上面写着一段让服务器关机的代码。之后客户端访问这个jsp,jsp里边的代码就会执行。

为了保证服务器的安全,把上传文件的目录放在用户访问不到的地方。WEB-INF下

File uploadedFile=new File(this.getServletContext().getRealPath("/WEB-INF/uploadedfile"));

3.如果上传的文件名称相同,以前的文件会被覆盖

让文件名唯一即可

filename=UUID.randomUUID()+"_"+filename;

4.把文件都放到一个文件夹里到时候检索的时候会很慢

把文件打散,弄一些子目录

  • 通过日期打散
File uploadedFile=new File(this.getServletContext().getRealPath("/WEB-INF/uploadedfile"));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String date=sdf.format(new Date());
uploadedFile=new File(uploadedFile,date);
  • 通过哈希值打散
int hashcode=fileName.hashCode();//返回此对象的哈希值
String code=Integer.toHexString(hashcode);//将其转换为16进制的字符,se3343ie
String childDirectory=code.charAt(0)+File.separator+code.charAt(1);// s/e
uploadedFile=new File(uploadedFile,childDirectory);

5.限制文件的大小

ServletFileUpload中的

  • setFileSizeMax(字节):设置单个文件的大小
  • setSizeMax(字节):总文件大小(多文件上传)

6.用户没有选择文件,就点击了上传

判断文件名是否为空即可

7.文件名为中文时会乱码。

用ServletFileUpload的setHeaderEncoding方法设置一下。

8.普通字段的value为中文时会乱码

String value=item.getString("utf-8");

用Streaming API实现上传

前面用的是传统的API实现上传,commons-fileupload还有一套Streaming API。官方文档http://commons.apache.org/proper/commons-fileupload/streaming.html说这种方法没有传统方法便利,但性能更好。传统方法有多便利我没看出来(有知道的,麻烦告诉我一下),但Streaming API在性能上的提升确可以明显的看出来。传统方法中,上传的文件都要先存储到内存或临时文件中,然后在对内存或临时文件进行操作。这样的话,如果我上传的大文件,相当于操作了文件两遍,先把上传文件弄到缓存文件中,再把缓存文件复制一份到最终目录。如果用Streaming API的话,直接把上传的文件传到最终的目录。

public class UploadServlet2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if (ServletFileUpload.isMultipartContent(request)) {

            ServletFileUpload upload=new ServletFileUpload();
            try {
                FileItemIterator iter = upload.getItemIterator(request);

                while(iter.hasNext()){
                    FileItemStream item=iter.next();
                    String name=item.getFieldName();//获取name属性的值

                    if(item.isFormField()){
                        processFormField(item);
                    }else{
                        processUploadedFile(item);
                    }
                }

            } catch (FileUploadException e) {

                e.printStackTrace();
            }


        }else {
            System.out.println("这不是文件上传表单");
        }

    }

    private void processUploadedFile(FileItemStream item) throws IOException{

        InputStream is=item.openStream();
        FileOutputStream fos=new FileOutputStream(new File("D:/"+item.getName()));

        int len=-1;
        byte[] b=new byte[1024*1024];
        while((len=is.read(b))!=-1){
            fos.write(b, 0, len);
        }
        is.close();
        fos.close();

    }

    private void processFormField(FileItemStream item) throws IOException {
        String name=item.getFieldName();
        InputStream is=item.openStream();
        String value=Streams.asString(is);


        System.out.println("name:"+name+"---"+"value:"+value);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值