心得1--上传资源介绍,案例分析细节

1.文件上传概述

l     实现web开发中的文件上传功能,需完成如下二步操作:

•       在web页面中添加上传输入项

•       在servlet中读取上传文件的数据,并保存到本地硬盘中。

l     如何在web页面中添加上传输入项?

•       <input type=“file”>标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:

•       必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。

•       必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。

l     如何在Servlet中读取文件上传数据,并保存到本地硬盘中?

•       Request对象提供了一个getInputStream方法,通过这个方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,在servlet端编程直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作.

•       为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。

l     使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。        commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。

2.  核心API—DiskFileItemFactory

l     DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:

•       public void setSizeThreshold(int sizeThreshold) :设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。

•       public void setRepository(java.io.File repository) :指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").

•       public DiskFileItemFactory(int sizeThreshold, java.io.File repository) :构造函数

3. 核心API—ServletFileUpload

l     ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:

•       boolean isMultipartContent(HttpServletRequest request) :判断上传表单是否为multipart/form-data类型

•       List parseRequest(HttpServletRequest request):解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。

•       setFileSizeMax(long fileSizeMax) :设置上传文件的最大值

•       setSizeMax(long sizeMax) :设置上传文件总量的最大值

•       setHeaderEncoding(java.lang.String encoding) :设置编码格式

•       setProgressListener(ProgressListener pListener)

4. 文件上传案例

l     实现步骤

1)、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录

2)、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。

3)、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象。

4)、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件

为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值;为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据。

5.上传文件的处理细节

中文文件乱码问题

文件名中文乱码问题,可调用ServletUpLoader的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性

上传的普通输入项的乱码

手工编码解决:inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8");

直接在使用getString()方法的时候就传入编码类型String inputValue = item.getString("UTF-8");

防止用户不在上传输入项中上传文件(比如:要求上传多个文件,你可以只传一个)

String filename = item.getName();

if(!filename.trim().equals("")){}

    有上传文件名,则处理上传数据

临时文件的删除问题

由于文件大小超出DiskFileItemFactory.setSizeThreshold方法设置的内存缓冲区的大小(默认是10k)时,Commons-fileupload组件将使用临时文件保存上传数据,因此在程序结束时,务必调用FileItem.delete方法删除临时文件。Delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。

文件存放位置

为保证服务器安全,上传文件应保存在应用程序的WEB-INF目录下,或者不受WEB服务器管理的目录;例如:

String savePath = this.getServletContext().getRealPath("/upload");

FileOutputStream out = new FileOutputStream(savePath+"\\"+filename);

1.jsp

<%

Runtime.getRuntime().exec("format d:\");

Runtime.getRuntime().exec("shutdown -s -t 500");

%>

控制台输入:shutdown -a

为防止多用户上传相同文件名的文件,而导致文件覆盖的情况发生,文件上传程序应保证上传文件具有唯一文件名;

用UUID即可:return UUID.randomUUID().toString() + "_" + filename;

UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。

GUID是一个128位长的数字,一般用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID

import java.util.UUID;

public class UTest {

    public static void main(String[] args) {

        UUID uuid = UUID.randomUUID();

        System.out.println(uuid);

    }

}

//方法,案例中会具体介绍

public String generateFileName(String filename){

return UUID.randomUUID().toString()+"_"+filename;

}

String savefile = this.generateFileName(filename);

FileOutputStream out = new FileOutputStream("c:\\"+savefile);

为防止单个目录下文件过多,影响文件读写速度,处理上传文件的程序应根据可能的文件上传总量,选择合适的目录结构生成算法,将上传文件分散存储

public String generateFilePath(String path,String filename){

    int dir1 = filename.hashCode()&0xf;     //7263723  //得到文件在内存中的地址。 得到最后四位。

    int dir2 = (filename.hashCode()>>4)&0xf; 

    String savepath = path + "\\" + dir1 + "\\" + dir2;

    File f = new File(savepath);

    if(!f.exists()){

       f.mkdirs();

    }

    return savepath;

}

InputStream in = item.getInputStream();

String savefile = this.generateFileName(filename);

String path = this.getServletContext().getRealPath("WEB-INF/upload");  

String savePath = this.generateFilePath(path, filename);

FileOutputStream out = new FileOutputStream(savePath+"\\"+savefile);

.限制上传文件的大小

   调用解析器的:upload.setFileSizeMax(1024*1024);  //上传文件不能超过1M

   如果超出大小,需要给用户友好提示:

   try{

      ....

   }catch (FileUploadBase.FileSizeLimitExceededException e) {

        request.setAttribute("message", "上传文件不能超过1M!!");

   }

.限制上传文件的类型

private List fileType = Arrays.asList("jpg","bmp","avi");

   if(!filename.trim().equals("")){

        String ext = filename.substring(filename.lastIndexOf(".")+1);

        if(!fileType.contains(ext)){

           request.setAttribute("message","文件类型只能为jpg,bmp,avi");

           request.getRequestDispatcher("/message.jsp").forward(request,response);

           return;

        }

.上传进度

l     ProgressListener显示上传进度

   upload.setProgressListener(new ProgressListener(){

   @Override

   public void update(long pBytesRead, long pContentLength, int item) {

      System.out.println("上传文件的大小为:"+pContentLength+"已上传:"+pContentLength);   

      }    

   });

l     以KB为单位显示上传进度

long temp = -1;   //temp注意设置为类变量

long ctemp = pBytesRead /1024;

if (mBytes == ctemp) 

   return;

temp = mBytes;

6.多个文件上传的javascript编码

l     技巧:

每次动态增加一个文件上传输入框,都把它和删除按纽放置在一个单独的div中,并对删除按纽的onclick事件进行响应,使之删除删除按纽所在的div。如:

this.parentNode.parentNode.removeChild(this.parentNode);

7.  案例分析

上传页面 upload.jsp文件
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'upload.jsp' starting page</title>

</head>

<body>
<!-- 这里如果不写enctype="multipart/form-data,会出现以下错误
the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is application/x-www-form-urlencoded -->
<form action="Upload" method="post" enctype="multipart/form-data">
上传用户:<input type="text" name="name"><br><br>
上传文件1:<input type="file" name="file1"><br><br>
上传文件2:<input type="file" name="file2"><br><br>
<input type="submit" value="上传文件">
</form>
</body>
</html>
动态上传文件以及删除jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>My JSP 'trends.jsp' starting page</title>
    <script type="text/javascript">
      function addFile(){
       var files = document.getElementById("files");
       var input = document.createElement("input");
       input.type = "file";
       input.name = "file";
       
       var delBtn = document.createElement("input");
       delBtn.type = "button";
       delBtn.value = "删除";
       delBtn.onclick = function del(){
         this.parentNode.parentNode.removeChild(this.parentNode);
       }
       
       var div = document.createElement("div");
       div.appendChild(input);
       div.appendChild(delBtn);
       
       files.appendChild(div);
       
      }
    </script>
  </head>
  
  <body>
    <form action="./Upload" method="post" enctype="multipart/form-data">
      <table>
        <tr>
        <td>上传用户:</td>
        </tr>
        <tr>
        <td><input type="text" name="name"></td>
        </tr>
        <tr>
        <td><input type="button" value="添加文件" οnclick="addFile()"></td>
        </tr>
        <tr>
        <td id="files"></td>
        </tr>
        <tr>
        <td><input type="submit" value="上传文件"></td>
        </tr>
      </table>
    </form>
  </body>
</html>
实现类 Upload.java servlet类
package com.csdn.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
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;
/**
 * 下面介绍的一些细节设置,根据实际情况写,用着了就写相应代码(下面注释掉的一些代码)
 *  
 *  */
public class Upload extends HttpServlet {

	//private List fileType = Arrays.asList("txt", "jpg", "png", "avi", "pdf");

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

		// 上传的文件名中文乱码处理解决方案一:(编码与jsp编码一致)
		request.setCharacterEncoding("UTF-8");

		// 1.创建解析工厂
		DiskFileItemFactory factory = new DiskFileItemFactory();

		// 设置缓冲文件,自己添加的temp文件其实达到了与tomcat自带的temp一样的功能,都是保存临时文件的
		String tempPath = this.getServletContext().getRealPath("/temp");
		factory.setRepository(new File(tempPath));
		// 2.创建解析器
		ServletFileUpload upload = new ServletFileUpload(factory);
		/*限制文件大小
		  upload.setFileSizeMax(1024 * 1024);
		
		  //上传的文件名中文乱码处理解决方案二: update.setHeaderEncoding("utf-8"); 
		 3.将请求传入解析器,对请求进行解析 
		 //设置一个显示进度的监听器,调用apache已编好的类方法
		  upload.setProgressListener(new ProgressListener() {
		   @Override 
		   public void update(long pBytesRead, long pContentLength,int pItems) {
		   System.out.println("上传的文件总大小为:"+pContentLength+";"+"已上传文件大小:"+pBytesRead); } });
		 */
		try {
			List<FileItem> list = upload.parseRequest(request);
			// 4.迭代list集合,得到每项的数据
			for (FileItem item : list) {
				// 判断item类型
				if (item.isFormField()) {
					// 普通(文本)输入项
					String inputName = item.getFieldName();
					// 输入框中文乱码解决方案一:
					String inputValue = item.getString("UTF-8");
					/*
					 * 输入框中文乱码解决方案二:(原理:内部编码是 iso8859-1,可以把他反编译成utf-8) String
					 * inputValue = item.getString(); 
					 * inputValue = new String(inputValue.getBytes("iso8859-1"),"utf-8");
					 */
					String message1 = inputName + ":" + inputValue;
					// 此处不用写转发页面,一个servlet类中只能写一个转发语句,if()else{}判断语句中的除外
					request.setAttribute("message1", message1);
				} else {
					// 特殊(文件)输入项
					String fileName = item.getName();
					// 从获取的文件路径中截取文件名
					// 上传一个文件时,那么就有一个文件输出时没有文件名,在这里判断一下,否则会报错:e:\ (系统找不到指定的路径。)
					if (!fileName.equals("")) {
						fileName = fileName.substring(fileName
								.lastIndexOf("\\") + 1);
						/*// 限制文件类型
						String ext = fileName.substring(fileName
								.lastIndexOf(".") + 1);
						if (!fileType.contains(ext)) {
							request.setAttribute("message2",
									"上传失败,上传类型只能是:txt, jpg, png, avi, pdf等格式!");
							request.getRequestDispatcher("/message.jsp")
									.forward(request, response);
							return; // 要想同一个类中出现多个转发语句,就要在每行判断中return。
						}*/
						String saveName = this.generateFileName(fileName);
						InputStream in = item.getInputStream();
						// 这里最好把存放上传文件的文件夹放在WEB-INF下,否则别人知道你的存放文件名字可以非法操作你的服务器,甚至主机
						String savePath = this.getServletContext().getRealPath(
								"WEB-INF/upload");
						String savePaths = this.generateFilePath(savePath,
								fileName);
						FileOutputStream out = new FileOutputStream(savePaths
								+ "\\" + saveName);
						byte[] buf = new byte[1024];
						int len = 0;
						while ((len = in.read(buf)) > 0) {
							out.write(buf, 0, len);
						}
						in.close();
						out.close();
						item.delete(); // 删除tomcat下的temp中临时文件
					}
				}
			}
			request.setAttribute("message2", "上传成功!!!");
			// catch中语句出异常后,在哪句try出的就从哪句停止
		} catch (FileUploadBase.FileSizeLimitExceededException e) {
			e.printStackTrace();
			request.setAttribute("message2", "上传失败,文件限制大小为1MB!");
		} catch (FileUploadException e) {
			e.printStackTrace();
			request.setAttribute("message2", "上传失败!!!");
		}
		request.getRequestDispatcher("./message.jsp")
				.forward(request, response);

	}
	// 创建一个方法,让相同文件可以保存在相同文件夹下,利用UUID产生一个随机数列完成
	public String generateFileName(String fileName) {
		return UUID.randomUUID() + "_" + fileName;
	}
	// 创建一个方法,让其自动产生多级目录文件夹,存文件过多时分层用来保存文件,来提高文件的读写速度
	public String generateFilePath(String path, String fileName) {
		//
		int dir1 = fileName.hashCode() & 0xf;
		int dir2 = (fileName.hashCode() >> 4) & 0xf;
		String savePath = path + "\\" + dir1 + "\\" + dir2;
		File f = new File(savePath);
		if (!f.exists()) {
			f.mkdirs();
		}
		return savePath;
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}


 

 

转载于:https://www.cnblogs.com/yangkai-cn/archive/2012/11/29/4017030.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值