Java文件上传下载

最近在工作中遇到文件上传下载问题,因此就在这里记一下

在文件上传下载中,文件是已流的方式在浏览器和服务器之间进行传递的。在apache的开源工具中common-fileupload这个组件是专门来处理文件上传下载的,也是目前使用的最多的一种方式,在这需要导入common-fileupload和common-io两个jar包,好了,下面来走起

一、文件上传

package com.file.Servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
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.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/*
 * 
 * Java、js文件上传(无框架)
 */
public class FileUploadServlet extends HttpServlet {
	/*注意的小细节问题
	 *	1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下(服务器硬盘下)。
       *	2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
 	 *	3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
  	 *	4、要限制上传文件的最大值。
 	 *	5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//设置字符编码
		request.setCharacterEncoding("utf-8");
		response.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		/** 设置响应头允许ajax跨域访问 **/  
        response.setHeader("Access-Control-Allow-Origin", "*");  
        /* 星号表示所有的异域请求都可以接受, */  
        response.setHeader("Access-Control-Allow-Methods", "GET,POST"); 
		
        // 检测是否为多媒体上传
        if (!ServletFileUpload.isMultipartContent(request)) {
            // 如果不是则停止
            out.write("{'error': '表单必须包含 enctype=multipart/form-data'}");
            out.flush();
            return;
        }
        System.out.println("------------------------");
        //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        //设置上传时生成的临时文件保存目录
        String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
        File tempFile = new File(tempPath);
        //判断文件上传目录是否存在,不存在则创建
        if(!tempFile.exists()) {
        	//创建文件目录
        	tempFile.mkdirs();
        }
        
        //配置文件上传
        try {
        	//使用Apache文件上传组件处理文件上传步骤:
        	//1、创建DiskFileItemFactory工厂
        	DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        	//1.2、设置工厂缓存区大小,当上传文件大小超过工厂缓存区大小时,就会生成临时文件存放在临时目录中
        	diskFileItemFactory.setSizeThreshold(1024 * 1024 * 3);//设置缓冲区的大小为3MB,如果不指定,那么缓冲区的大小默认是10KB
        	//1.3、设置上传临时文件的保存目录
        	diskFileItemFactory.setRepository(tempFile);
        	
        	//2、创建文件解析器
        	ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
        	//2.1、解决上传文件编码问题
        	servletFileUpload.setHeaderEncoding("utf-8");
        	//2.2、监听文件上传进度
        	servletFileUpload.setProgressListener(new ProgressListener() {
				@Override
				public void update(long readNum, long fileSize, int arg2) {
					//System.out.println("文件大小:"+fileSize+",当前已处理:"+readNum);
				}
        		
        	});
        	//2.3、设置上传单个文件的大小的最大值(40MB)
        	servletFileUpload.setFileSizeMax(1024 * 1024 * 40); 
        	//2.4、设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和(60MB)
        	servletFileUpload.setSizeMax(1024*1024*60);
        	
        	//3、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
        	List<FileItem> listItems = servletFileUpload.parseRequest(request);
        	 //判断文件中是否存放数据
        	if(listItems.size() > 0 && listItems != null) {
        		//3.1、迭代表单数据
        		for(FileItem item : listItems) {
            		//3.2如果fileitem中封装的是普通输入项的数据(form表单中的不是上传文件的其他参数)
        			if(item.isFormField()) {
        				String fieldName = item.getFieldName();
        				//解决普通输入项的数据的中文乱码问题
        				String value = item.getString("UTF-8");
        				System.out.println("fieldName:"+fieldName+",value:"+value);
        			}else {
        				//3.3、如果fileitem中封装的是上传文件
        				//3.3.1、得到上传的文件名称,
        				String fileName = item.getName();
        				if(fileName == null || fileName.trim().equals("")) {
        					continue;
        				}
        				//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:  c:\a\b\1.txt,
        				//而有些只是单纯的文件名,如:1.txt
        				//3.3.2、处理获取到的上传文件的文件名的路径部分,只保留文件名部分
        				fileName = fileName.substring(fileName.lastIndexOf("\\")+1);
        				//得到上传文件的扩展名(可用来限制上传的文件类型)
        				String fileExitName = fileName.substring(fileName.lastIndexOf(".")+1);
        				System.out.println("上传的文件的扩展名是:"+fileExitName);
        				//得到文件唯一的名称
        				String onlyFileName = onlyFileName(fileName);
        				//得到文件存储目录
        				String onlyFilePath = onlyFilePath(onlyFileName,savePath);
        				System.out.println("文件存储目录:"+onlyFilePath+File.separator+onlyFileName);
        				//3.3.3、获取item中的上传文件的输入流
        				InputStream is = item.getInputStream();
        				//创建输出流
        				FileOutputStream fos = new FileOutputStream(new File(onlyFilePath+File.separator+onlyFileName));
        				//创建一个缓冲区
        				byte buffer[] = new byte[1024];
        				//判断输入流中的数据是否已经读完的标识
        				int len = 0;
        				//循环将输入流读入到缓冲区当中,(len=is.read(buffer))>0就表示in里面还有数据
        				while((len = is.read(buffer)) > 0) {
        					fos.write(buffer, 0, len);
        					fos.flush();
        				}
        				//关闭输入输出流
        				is.close();
        				fos.close();
        				//删除处理文件上传时生成的临时文件
        				item.delete();
        			}
            	}// end for
        	}//end if
        	
        	
        }catch(Exception e) {
        	System.out.println("文件上传失败");
        }
		
	}
	/**
	 * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
	 * @param fileName  原始文件名
	 * @return   新文件名
	 */
	public String onlyFileName(String fileName) {
		return UUID.randomUUID()+"_"+fileName;
	}
	
	/**
	 * @Description: 为防止一个目录下面出现太多文件,要使用hash算法打散存储
	 * @param fileName	文件名,要根据文件名生成存储目录
	 * @param filePath	文件存储路径
	 * @return	新的存储目录
	 */
	public String onlyFilePath(String fileName,String filePath) {
		//得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
		int hashCode = fileName.hashCode();
		int addr1 = hashCode&0xf;
		int addr2 = (hashCode&0xf)>>4;
		//构造新的保存目录
		String path=filePath+File.separator+addr1+File.separator+addr2;
		File file = new File(path);
		//如果目录不存在则创建文件
		if(!file.exists()) {
			file.mkdirs();
		}
		return path;
	}
	
}

 

二、文件下载

package com.file.Servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
 * 
 * Java、js文件下载(无框架)
 */
public class FileDownloadServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//设置字符编码
		request.setCharacterEncoding("utf-8");
		response.setCharacterEncoding("utf-8");
		response.setContentType("multipart/form-data");
		
		/** 设置响应头允许ajax跨域访问 **/  
        response.setHeader("Access-Control-Allow-Origin", "*");  
        /* 星号表示所有的异域请求都可以接受, */  
        response.setHeader("Access-Control-Allow-Methods", "GET,POST"); 
        
        //1、得到要下载的文件名(经过UUID得到的文件名)
        String fileName  = request.getParameter("fileName");
        //2、得到将要下载文件的目录
        String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        //3、通过文件名找出文件的所在目录(也可以在上传时存放在数据库,现在去查找数据库)
        String findOnlyFilePath = findOnlyFilePath(fileName,savePath);
        File file = new File(findOnlyFilePath+File.separator+fileName);
        if(!file.exists()) {
        	System.out.println("您要下载的资源已被删除!!!");
        	return;
        }
        //4、处理文件名(因为文件名是经过唯一的处理的所以在这里需要处理一下)
        String realname = fileName.substring(fileName.indexOf("_")+1);
        //5、设置响应头,控制浏览器下载该文件
        response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(realname, "UTF-8"));
        //6、读取要下载的文件,保存到文件输入流
        FileInputStream fis = new FileInputStream(file);
        //创建输出流
        OutputStream os = response.getOutputStream();
        //创建缓冲区
        byte buffer[] = new byte[1024];
        int len = 0;
        //循环将输入流中的内容读取到缓冲区中
        while((len=fis.read(buffer)) > 0) {
        	//输出缓冲区的内容到浏览器,实现文件下载
        	os.write(buffer,0,len);
        	os.flush();
        }
        //7、关闭输入流输出流
        fis.close();
        os.close();
	}
	/**
	 * @Description: 通过文件名和存储上传文件根目录找出要下载的文件的所在路径
	 * @param fileName	文件名,要根据文件名获得存储目录
	 * @param filePath	文件存储路径
	 * @return	新的存储目录
	 */
	public String findOnlyFilePath(String fileName,String filePath) {
		//得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
		int hashCode = fileName.hashCode();
		int addr1 = hashCode&0xf;
		int addr2 = (hashCode&0xf)>>4;
		//构造新的保存目录
		String path=filePath+File.separator+addr1+File.separator+addr2;
		File file = new File(path);
		//如果目录不存在则创建文件
		if(!file.exists()) {
			file.mkdirs();
		}
		return path;
	}
	
}

  三、文件查询(这里可以查询数据库也可以根据文件目录查询本地文件,这里是第二种)

package com.file.Servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

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

import net.sf.json.JSONObject;


public class SearchFileServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		super.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//设置编码
		request.setCharacterEncoding("utf-8");
		response.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		
		//设置ajax跨域访问
        response.setHeader("Access-Control-Allow-Origin", "*");  
        /* 星号表示所有的异域请求都可以接受, */  
        response.setHeader("Access-Control-Allow-Methods", "GET,POST");
        
        String username = request.getParameter("username");
        System.out.println(username);
        //获取上传文件的目录
        String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
        //存储经过UUID生成的唯一文件名、实际的文件名
        Map<String,String> fileNameMap = new HashMap<String,String>();
        //创建上传文件的目录
        File file = new File(uploadFilePath);
        //递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中
        traverseFile(file,fileNameMap);
        //将数据转成json格式返回前台
        JSONObject jsonObject = JSONObject.fromObject(fileNameMap);  
        out.write(jsonObject.toString());
	}
	public void traverseFile(File file,Map<String,String> map) {
		 //如果file代表的不是一个文件,而是一个目录
        if(!file.isFile()) {
        	//列出该目录下的所有文件和目录
        	File[] listFiles = file.listFiles();
        	//递归遍历
        	for(File fil:listFiles) {
        		traverseFile(fil,map);
        	}
        }else {//如果file代表的是一个文件,而不是一个目录
        	//处理文件名,上传后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid_部分(文件名类似于:9349249849-88343-8344_不差钱儿.avi)
        	String fileName = file.getName().substring(file.getName().indexOf("_")+1);
        	//file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复,因此作为值
        	map.put(file.getName(), fileName);
        }
	}
	
}

四、xml配置

  <!-- 文件上传 -->
  <servlet>
  	<servlet-name>fileUpload</servlet-name>
  	<servlet-class>com.file.Servlet.FileUploadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>fileUpload</servlet-name>
  	<url-pattern>/fileUpload</url-pattern>
  </servlet-mapping>
  <!-- 文件下载 -->
  <servlet>
  	<servlet-name>fileDownload</servlet-name>
  	<servlet-class>com.file.Servlet.FileDownloadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>fileDownload</servlet-name>
  	<url-pattern>/fileDownload</url-pattern>
  </servlet-mapping>
  <!-- 文件查询 -->
  <servlet>
  	<servlet-name>fileSearch</servlet-name>
  	<servlet-class>com.file.Servlet.SearchFileServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>fileSearch</servlet-name>
  	<url-pattern>/fileSearch</url-pattern>
  </servlet-mapping>

好了,上面就是Java文件上传下载的全部了。

 

配合Java使用的js文件上传下载:https://blog.csdn.net/little__SuperMan/article/details/82900820

 

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值