Java文件的上传和下载

一、文件上传

1、文件上传的原理分析

把文件从客户端传递到服务器的过程就是文件上传。

1.1文件上传的必要前提:

a、提供form表单,method必须是post

b、form表单的enctype必须是multipart/form-data

c、提供input type="file"类的上传输入域

1.2enctype属性

作用:告知服务器请求正文的MIME类型。(请求消息头:Content-Type作用是一致的)

可选值:

l  application/x-www-form-urlencoded(默认):

正文:name=admin&password=123

服务器获取数据:String name =request.getParameter("name");

l  multipart/form-data:

正文:

Java文件的上传和下载_第1张图片

服务器获取数据:request.getParameter(String)方法获取指定的表单字段字符内容,但文件上传表单已经不在是字符内容,而是字节内容,所以失效。

文件上传:解析请求正文的每部分的内容。

2、借助第三方的上传组件实现文件上传

2.1 fileupload概述

fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。

导入commons-fileupload相关jar包

l  commons-fileupload.jar,核心包;

地址:http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi

l  commons-io.jar,依赖包。

地址:http://commons.apache.org/proper/commons-io/download_io.cgi

2.2 fileupload的核心类有:

DiskFileItemFactory、ServletFileUpload、FileItem。

a、解析原理

Java文件的上传和下载_第2张图片

 

2.2 fileupload简单应用

使用fileupload组件的步骤如下:

1.      创建工厂类DiskFileItemFactory对象:

DiskFileItemFactory factory = new DiskFileItemFactory()

2.      使用工厂创建解析器对象:

ServletFileUpload fileUpload = new ServletFileUpload(factory)

3.      使用解析器来解析request对象:

List list = fileUpload.parseRequest(request)


FileItem对象对应一个表单项(表单字段)。可以是文件字段或普通字段

l  boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;

l  String getFieldName():获取字段名称,例如:,返回的是username;

l  String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;

l  String getName():获取文件字段的文件名称;(a.txt)

l  String getContentType():获取上传的文件的MIME类型,例如:text/plain。

l  int getSize():获取上传文件的大小;

l  InputStream getInputStream():获取上传文件对应的输入流;

l  void write(File):把上传的文件保存到指定文件中。

l  delete();删除临时文件

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
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.FileUploadBase;
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.FilenameUtils;

public class UploadServlet2 extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@SuppressWarnings("unchecked")
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 判断表单的提交类型是否是enctype="multipart/form-data"
		boolean isMultipart = ServletFileUpload.isMultipartContent(request);
		if (isMultipart) {
			try {
				// 创建一个解析器工厂类
				DiskFileItemFactory factory = new DiskFileItemFactory();
				//factory.setRepository(new File("f:/"));//把临时文件保存到f盘下
				// 通过工厂创建解析器
				ServletFileUpload upload = new ServletFileUpload(factory);
				upload.setHeaderEncoding("UTf-8");// 解决上传文件名乱码问题
				// 限制单个文件上传的大小
				upload.setFileSizeMax(1024 * 1024 * 3);// 文件限制为3M
				// 限制文件上传的总大小
				upload.setSizeMax(1024*1024*6);//文件总大小不能超过6M
				// 解析request对象
				List fileItems = upload.parseRequest(request);

				for (FileItem fileItem : fileItems) {
					if (fileItem.isFormField()) {// 判断是否是普通表单项(true:是普通表单项)
						processFormField(fileItem);
					} else {
						processUploadFile(fileItem);
					}
				}

			} catch (FileUploadBase.FileSizeLimitExceededException e) {
				System.out.println("上传文件过大,不能超过3M");
			} catch (FileUploadBase.SizeLimitExceededException e) {
				System.out.println("上传文件过大,总大小不能超过6M");
			} catch (FileUploadException e) {
				e.printStackTrace();
			}
		}
	}

	// 解析上传文件表单项
	private void processUploadFile(FileItem fileItem) {
		try {
			// 得到name的值 name="photo"
			String name = fileItem.getFieldName();
			// 得到文件名
			String filename = fileItem.getName(); // F:\图片素材\有范儿\25.jpg 或 25.jpg
			
			// 文件内容
			InputStream in = fileItem.getInputStream();

			System.out.println(name + "\t" + filename);

			// 创建一个目录 File即代表文件又代表目录
			File storeDirectory = new File(this.getServletContext()
					.getRealPath("/upload"));
			if (!storeDirectory.exists()) {
				storeDirectory.mkdirs();// 创建目录
			}

			// 解决文件名路径问题
			// filename = filename.substring(filename.lastIndexOf("/")+1);
			filename = FilenameUtils.getName(filename);
			//得到文件的扩展名
			String extension = FilenameUtils.getExtension(filename);
			//只能上传jpg和txt文件
			/*if(!("jpg".equals(extension)||"txt".equals(extension))){
				return;
			}*/
			// 解决文件名冲突的问题
			filename = UUID.randomUUID() + "_" + filename;

			// 创建子目录(目录打散)
			String childDirectory = makeChildDirectory1(storeDirectory,
					filename);
			// 创建文件
			File file = new File(storeDirectory, childDirectory + "/"
					+ filename); // F:\\apache-tomcat-7.0.52\\webapps\\day20_00_upload\\upload\\F:\\图片素材\\有范儿\\25.jpg

			// 创建一个文件输出流
			fileItem.write(file);
			
			 /* OutputStream out = new FileOutputStream(file); int len = 0;
			  byte[] b = new byte[1024]; while((len=in.read(b))!=-1){
			  out.write(b, 0, len); }
			  
			  out.close(); 
			  in.close();*/
			 
			fileItem.delete();//删除临时文件
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 创建子目录2
	private String makeChildDirectory1(File storeDirectory, String filename) {
		int hashCode = filename.hashCode();// 得到一个32位的整数
		String hexString = Integer.toHexString(hashCode);// 把整数转换成16进制的字符串
															// 65fsad32fhf
		String child = hexString.substring(0, 1) + "/"
				+ hexString.substring(1, 2); // 6/5
		File childDirectory = new File(storeDirectory + "/" + child);
		if (!childDirectory.exists()) {
			childDirectory.mkdirs();
		}
		return child;
	}

	// 创建子目录(以日期为单位)
	private String makeChildDirectory(File storeDirectory) {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		String fdate = sdf.format(date); // 2016-05-07
		// 创建目录
		File childDirectory = new File(storeDirectory + "/" + fdate);
		if (!childDirectory.exists()) {
			childDirectory.mkdirs();
		}
		return fdate;
	}

	// 解析普通表单项
	private void processFormField(FileItem fileItem) {
		try {
			String name = fileItem.getFieldName();// 得到name="name"
			String value = fileItem.getString("UTF-8");// 得到value
			System.out.println(name + "=" + value);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

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

}

二、文件的下载

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DownloadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=GBK");
		String filename = "销售榜单.csv";
		
		//解决文件名不安全的问题
		filename = URLEncoder.encode(filename, "UTF-8");
		
		//告知客户端浏览器下载文件
		response.setHeader("content-disposition", "attachment;filename="+filename);
		//告知浏览器下载文件的格式
                //例:images/jpeg 作用:this.getServletContext().getMimeType(filename)得到文件类型 		
                response.setHeader("content-type", this.getServletContext().getMimeType(filename)); 
		
		PrintWriter out = response.getWriter();
		out.write("id,name,price\n");
		out.write("1,冰箱,200\n");
		out.write("2,电话机,300\n");
		out.write("3,复读机,200\n");
		
	}

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

三、Notice

a、文件名处理的问题   得到简单的没有路径文件名31.jpg

// 解决文件名路径问题  filename = filename.substring(filename.lastIndexOf("/")+1);
filename = FilenameUtils.getName(filename);

b、避免文件被覆盖,让文件名唯一即可

// 解决文件名冲突的问题
filename = UUID.randomUUID() + "_" + filename;

c、避免同一个文件夹中的文件过多

方案一:按照日期进行打散存储目录

// 创建子目录(以日期为单位)
	private String makeChildDirectory(File storeDirectory) {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		String fdate = sdf.format(date); // 2016-05-07
		// 创建目录
		File childDirectory = new File(storeDirectory + "/" + fdate);
		if (!childDirectory.exists()) {
			childDirectory.mkdirs();
		}
		return fdate;
	}

方案二:用文件名的hashCode计算打散的存储目录:二级目录

// 创建子目录2
	private String makeChildDirectory1(File storeDirectory, String filename) {
		int hashCode = filename.hashCode();// 得到一个32位的整数
		String hexString = Integer.toHexString(hashCode);// 把整数转换成16进制的字符串 // 65fsad32fhf
		String child = hexString.substring(0, 1) + "/"
				+ hexString.substring(1, 2); // 6/5
		File childDirectory = new File(storeDirectory + "/" + child);
		if (!childDirectory.exists()) {
			childDirectory.mkdirs();
		}
		return child;
	}

d、限制文件的大小:web方式不适合上传大的文件

单个文件大小:   

ServletFileUpload.setFileSizeMax(字节)

总文件大小:(多文件上传)

ServletFileUpload.setSizeMax(字节)

// 创建一个解析器工厂类
DiskFileItemFactory factory = new DiskFileItemFactory();
//factory.setRepository(new File("f:/"));//把临时文件保存到f盘下
// 通过工厂创建解析器
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTf-8");// 解决上传文件名乱码问题
// 限制单个文件上传的大小
upload.setFileSizeMax(1024 * 1024 * 3);// 文件限制为3M
// 限制文件上传的总大小
upload.setSizeMax(1024*1024*6);//文件总大小不能超过6M
// 解析request对象
List fileItems = upload.parseRequest(request);

e、上传字段用户没有上传文件的问题

通过判断文件名是否为空即可

f、临时文件的问题

DiskFileItemFactory:

作用:产生FileItem对象

内部有一个缓存,缓存大小默认是10Kb。如果上传的文件超过10Kb,用磁盘作为缓存。

存放缓存文件的目录在哪里?默认是系统的临时目录。

如果自己用IO流实现的文件上传,要在流关闭后,清理临时文件。

FileItem.delete();

 
 

你可能感兴趣的:(Java文件的上传和下载)