文件上传:第(1)阶段,处理流程

1、浏览器的表单上传文件到Tomcat服务器时,表单中的数据(包括普通文本域,也包括文件)被封装成了request对象
2、要想保存这些表单数据(普通文本、文件),就必须把这些信息从request对象抽取出来
3、把这些表单数据抽取出来的一个类叫做ServletFileUpload。
4、ServletFileUpload有一个方法parseRequest(HttpServletRequest)(A list of FileItem instances parsed from the request)能够从request对象中抽取信息,该方法的返回值是List<FileItem>。
5、那么什么是FileItem呢?它代表了multipart/form-data POST request中的一个file 或者是一个 form item。
6、接下来,对每个FileItem进行处理,或者读取普通文本域,或者读取文件内容。


文件上传:第(2)阶段,构造ServletFileUpload

1、在第一阶段当中,得出这样的结论:通过ServletFileUpload的parseRequest(HttpServletRequest)方法,可以得到一系列的FileItem。
2、在执行parseRequest(HttpServletRequest)方法之前,需要先构造ServletFileUpload对象。
3、ServletFileUpload有一个构造方法ServletFileUpload(FileItemFactory fileItemFactory),它接受一个FileItemFactory类型的对象。
4、FileItemFactory是一个接口,它的作用是创建FileItem对象实例。
5、小结:ServletFileUpload的parseRequest(HttpServletRequest)方法得到List<FileItem>对象,但是FileItem的创建实际上是由FileItemFactory对象创建的。
6、FileItemFactory是一个接口,它一个默认实现类是DiskFileItemFactory类。


文件上传:第(3)阶段,初识FileItem

1、通过ServletFileUpload的parseRequest(HttpServletRequest)方法,可以得到一系列的FileItem。
2、那么应该如何处理FileItem呢?如何区分它是一个普通的表单项,还是一个文件呢?如果是文件,该如何保存呢?
3、区分FileItem是“普通表单项,还是文件?”的方法是isFormField()。如果是普通表单项,则返回ture,如果是上传的文件,则返回false。
4、对于普通表单项,要获取它的表单项的name和value,用到的方法分别是getFieldName()和getString(String encoding)。
5、对于文件,要获取它的表单项的name(field name)、文件的名称(file name)和实际上传的文件,最终是要将文件保存到磁盘上。获取表单项name(field name)的方法是getFieldName(),获取文件的名称(file name)用getName(),获取文件的输入流用getInputStream()方法。


涉及到的方法:


----------通用方法

a)判断是普通表单项,还是文件?

boolean isFormField()

Determines whether or not a FileItem instance represents a simple form field.

Returns: true if the instance represents a simple form field; false if it represents an uploaded file.


b)获取表单项的名字(包括普通表单项和文件)

String getFieldName()

Returns the name of the field in the multipart form corresponding to this file item.


----------处理普通表单项方法

a)获取普通表单项的内容

String getString(String encoding)

Returns the contents of the file item as a String, using the specified encoding. 


----------处理文件方法

a)得到文件的名字

String getName()

Returns the original filename


b)获取文件的输入流

InputStream getInputStream()

Returns an InputStream that can be used to retrieve the contents of the file.




文件上传:第(4)阶段,深入FileItem

1、处理【普通表单项】时,获取表单值用getString(String encoding),需要指定字符集(例如UTF-8)
        getString(String encoding),是需要开发人员指定字符集
        String getString(),使用“默认”的字符集
        这两个方法都是建立在byte[] get()方法之上的。
        如何查看“默认”的字符集呢?可以通过Charset.defaultCharset()查看。
2、处理【文件】时,如何确定是否上传文件?可以通过getSize()方法,如果返回的值为0,则表示没有上传文件。
3、处理【文件】时,可以通过getContentType()获取上传文件的一些类型信息,但是如果想判断上传的文件到底是什么类型,还是通过getName()得到文件名后,再截取文件名后缀得到。
4、处理【文件】时,生成的临时文件,如果比较小,则存储在内存当中;如果比较大,则存储在磁盘上。那么到底存储在什么位置呢?可以通过isInMemory()来进行判断。判断当前文件是大,还是小的域值,是在DiskFileItemFactory的构造方法中指定。isInMemory()和getSize()方法配合使用。
5、在进行保存【文件】时,可以使用IOUtils.copy(InputStream input, OutputStream output)的方法进行存储。也可以通过write(File file)写入到指定文件中。
6、在处理【文件】时,FileItem还提供了一个delete()方法,这个方法可以尽早的删除临时文件、释放系统资源。



涉及到的方法:


----------通用方法

a)得到普通表单项的值或者是文件的大小

long getSize()

Returns the size of the file item.


----------处理普通表单项方法

a)得到普通表单项的值,使用默认字符集

String getString()

Returns the contents of the file item as a String, using the default character encoding. This method uses get() to retrieve the contents of the item.


b)获取普通表单项的值的字节表示形式

byte[] get()

Returns the contents of the file item as an array of bytes.


----------处理文件方法


a)得到文件的类型

String getContentType()

Returns the content type passed by the browser or null if not defined.

文件:

.txt-->text/plain

.rar-->application/octet-stream

.html-->text/html

.jpg-->p_w_picpath/jpeg

.gif-->p_w_picpath/gif

.png-->p_w_picpath/png


b)是否位于内存当中isInMemory(),应该和getSize()配合使用吧

boolean isInMemory()

Provides a hint as to whether or not the file contents will be read from memory.


c)写入到文件

void write(File file)

A convenience method to write an uploaded item to disk. The client code is not concerned with whether or not the item is stored in memory, or on disk in a temporary location. They just want to write the uploaded item to a file. 


d)删除临时文件

void delete()

Deletes the underlying storage for a file item, including deleting any associated temporary disk file. Although this storage will be deleted automatically when the FileItem instance is garbage collected, this method can be used to ensure that this is done at an earlier time, thus preserving system resources.



文件上传:第(5)阶段,深入ServletFileUpload

1、如果要限制单个上传文件的大小,可以使用setFileSizeMax(long fileSizeMax)
2、限制上传文件的总大小,可以使用setSizeMax(long sizeMax)
3、setHeaderEncoding(String encoding)方法可以指定读取请求头的编码,它对于getFieldName()和getName()方法有效,但对于getString()方法无效,因为getString()总是使用默认的字符集进行编码,因此要使用getString(String encoding)来代替getString()方法。


a)设置上传单个文件的大小

setFileSizeMax(long fileSizeMax)

Sets the maximum allowed size of a single uploaded file


b)设置上传文件的总大小

setSizeMax(long sizeMax)

Sets the maximum allowed size of a complete request, as opposed to setFileSizeMax(long).


c)设置编码

setHeaderEncoding(String encoding)

Specifies the character encoding to be used when reading the headers of individual part. When not specified, or null, the request encoding is used. If that is also not specified, or null, the platform default encoding is used.


文件上传:第(6)阶段,总结

1、第(1)阶段,讲处理的流程,由request-->ServletFileUpload-->FileItem
2、第(2)阶段,讲如何构造ServletFileUpload对象
3、第(3)阶段,对FileItem进行一个初步了解,就可以实现普通表单项和文件的读取和保存了。
4、第(4)阶段,对FileItem进一步深入,更详细的解释几个API方法
5、第(5)阶段,对ServletFileUpload的限制文件大小和字符编码做了介绍
6、由(1)至(5)阶段,都是对mutipart/form-data的post类型的表单进行处理。那么如何判断一个表单是普通表单,还是带有文件上传功能的表单呢?ServletFileUpload提供了一个静态方法isMultipartContent(HttpServletRequest request),用于判断当前表单是否为multipart/form-data的post类型的表单。


boolean isMultipartContent(HttpServletRequest request) 

Utility method that determines whether the request contains multipart content.




文件上传示例

1、引入jar包

2、写jsp,用于在浏览器端选择上传文件

3、写servlet,用于在服务器处理上传文件的表单


1、引入jar包

commons-fileupload-1.3.1.jar
commons-io-2.4.jar

2、test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>上传文件示例</title>
</head>
<body>
	<form action="${pageContext.request.contextPath }/file" method="post" enctype="multipart/form-data">
		图书名称:<input type="text" name="bookname"/><br/>
		作者名称:<input type="text" name="username"/><br/>
		图书封面:<input type="file" name="bookcover"/><br/>
		<input type="submit" value="提交"/>
	</form>
</body>
</html>

3、FileServlet.java

package com.rk.web.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;

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

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

public class FileServlet extends HttpServlet
{
	private static final long serialVersionUID = -6986341005030926195L;

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		FileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload fileUpload = new ServletFileUpload(factory);
		fileUpload.setFileSizeMax(1024 * 1024 * 5);//设置单个上传文件的大小
		fileUpload.setHeaderEncoding("UTF-8");//设置字符编码
		try
		{
			List<FileItem> listOfFilItem = fileUpload.parseRequest(request);
			for(FileItem fileItem : listOfFilItem)
			{
				if(fileItem.isFormField())
				{
					String fieldName = fileItem.getFieldName();
					String fieldValue = fileItem.getString("UTF-8");
					System.out.println(fieldName + "=" + fieldValue);
				}
				else
				{
					/*上传文件的大小*/
					long size = fileItem.getSize();
					if(size == 0)
					{
						System.out.println("当前没有上传文件!");
						continue;
					}
					else
					{
						System.out.println("当前上传文件的大小为:" + size + "字节");
					}
					
					/*上传文件的ContentType*/
					String contentType = fileItem.getContentType();
					System.out.println("当前上传文件的ContentType为:" + contentType);
					
					/*form field name*/
					String fieldName = fileItem.getFieldName();
					System.out.println("当前上传文件的form field name为:" + fieldName);
					
					/*上传文件的名字*/
					String fileName = fileItem.getName();
					String fileExtention =  fileName.substring(fileName.lastIndexOf("."));
					System.out.println("当前上传文件的名称为:" + fileName + ",它的扩展名为:" + fileExtention);
					
					/*将文件保存至磁盘*/
					//a)获取InputStream
					InputStream inputStream = fileItem.getInputStream();
					//b)获取OutputStream
					String path = this.getServletContext().getRealPath("/upload"); //保存的目标文件夹
					File file = new File(path,fileName);					
					OutputStream outputStream = new FileOutputStream(file);
					//c)使用IOUtils进行拷贝
					IOUtils.copy(inputStream, outputStream);
					//d)关闭OutputStream
					outputStream.close();
				}
			}
		}
		catch (FileUploadException e)
		{
			e.printStackTrace();
		}
	}
	private String getUUID()
	{
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
}