web-上传文件

271 阅读6分钟

底层原理

https://zhangchong.github.io/2016/11/07/fileUpload/

总结

上传文件,本质上都是包含以几个步骤:
1.客户端向服务器发送http请求
2.客户端向请求路径写二进制数据
3.服务器读二进制数据
写数据到磁盘(即保存文件到服务器)
保存文件路径到数据库表的路径字段

下面介绍几个实际应用场景。

web

1.基于开源file-upload实现
封装了读二进制流的处理
最后,得到文件项集合List

2.基于struts2实现
也是基于开源file-upload
只不过结合struts2,封装得更加好用
最后,得到文件数组File[]

File[]是Action类的数据

3.基于springMVC实现
同struts2

合伙人二维码

功能描述

生成二维码图片

实现思路

1.第一次访问
1)生成二维码
保存二维码到服务器
2)保存二维码路径到数据库

2.第二次访问 根据数据库的路径字段,访问服务器二维码存放路径

代码

控制器

/**
	 * 
	 * <pre>
	 * 生成合伙人二维码
	 * </pre> 
	 * @author gzh
	 * @date 2017年3月30日下午8:33:29
	 */
	public String doGenerateHeHuoRenQRcode() throws IOException {
		try {
			ProxyUser proxyUser = (ProxyUser) getSessionAttribute("proxyUser");
			
			//判断是否已经生成二维码
			Proxy proxy2 = proxyService.getProxyById(proxyUser.getProxyId());
			if (proxy2.getQrCode() != null) { //已生成
				//输出数据
				String imgUrl = ConfigUtil.PROXY_LOOKPATH_QRCODE + File.separator + proxy2.getQrCode();
//				imgUrl = imgUrl.replaceAll("\\\\", "/");
				setRequestAttribute("imgUrl", imgUrl);
			}else { //未生成
				if(!isNotNullOrEmpty(proxyUser.getInviteCode())){
					proxyUserService.updateProxyUserInviteCode(proxyUser);
				}
				
				ProxyUser pu = proxyUserService.getProxyUser(proxyUser);
				
				// 生成二维码
				StringBuffer toHeHuoRenUrl = new StringBuffer(ConfigUtil.TO_HEHUOREN_URL_QRCODE); //代理商-生成二维码图片的路径
				//toHeHuoRenUrl.append(proxyUser.getId());
				toHeHuoRenUrl.append(pu.getInviteCode());
				String logoPath = "";// 二维码Logo
				StringBuffer imgPath = new StringBuffer(ConfigUtil.PROXY_UPLOADPATH_QRCODE);// 代理商-二维码图片的上传路径
				String date = DateUtils.getInstance().today1();
				
				//判断日期目录是否存在
				File dateDir = new File(imgPath + File.separator + date);
				if (!dateDir.exists()) { //不存在
					dateDir.mkdirs();
				}
				
				String imgName = date + File.separator + UuidGenerator.getUuidGenerator() + "." + IMAGE_TYPE; // 图片名称
				imgPath.append(File.separator).append(imgName);
				
				

				try {
					QRCodeComm.encoderQRCode(toHeHuoRenUrl.toString(), imgPath.toString(), IMAGE_TYPE, logoPath);
				} catch (Exception e) {
					log.error("HeHuoRen.doGenerateHeHuoRenQRcode() error " + e); 
					return "";
				}
				
				//保存二维码路径
				Proxy proxy = new Proxy();
				proxy.setId(proxyUser.getProxyId());
				proxy.setQrCode(imgName.toString());
				int result = proxyService.updateProxy(proxy);
				if (result == 0) {
					log.error("HeHuoRen.doGenerateHeHuoRenQRcode() error: update t_proxy failed!");
				}
				
				//输出数据
				String imgUrl = ConfigUtil.PROXY_LOOKPATH_QRCODE + File.separator + imgName;
				setRequestAttribute("imgUrl", imgUrl);
			}
		} catch (Exception e) {
			log.error("doGenerateHeHuoRenQRcode() error", e);
			return "";
		}
		return "success";
	}

app

进件

功能描述

向第三方支付通道进件 第三方支付向银行通道或其他第三方支付通道(例如,支付宝和微信)进件

实现思路

1.app发送请求
1)http请求路径
2)往请求路径写二进制流
3)http请求对象的内容类型content-type字段,需要设置为多媒体类型

2.后台接收请求
1)基于上传文件开源框架-apache fileupload读二进制流
上传文件框架会把二进制流转换为文件对象(框架里的文件对象,与jdk的File不是一个东西)
2)从文件对象取文件流
3)读文件流
4)写数据到服务器磁盘

代码

后台控制器代码
注:app发送http请求时,确保2点,1.往请求路径写二进制流数据 2.请求类型字段content-type的值,设置为多媒体类型

package com.dinpay.dpp.cpmobile.action.cpinfo;

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

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dinpay.dpp.pmsmobile.comm.PmsBaseAction;

/**
 * 
 * <pre>
 * 类的名称:
 * 类的作用:进件
 * 1.商家进件到第三方支付公司通道
 * 2.第三方支付公司通道再把数据进件到支付宝和微信
 * </pre>
 * 
 * @author gzh
 * @date 2017年12月21日下午12:00:47
 */
public class IntoPieceAction extends PmsBaseAction {
	/**
	 * 
	 */
	private static final long serialVersionUID = 5706369770419859939L;
	
	private static Log log = LogFactory.getLog(PayInAction.class);

	/**
	 * 
	 * <pre>
	 * 进件
	 * 一、上传图片
	 * 1.读文件流数据
	 * 2.写数据到本地磁盘(即保存文件到本地服务器)
	 * 3.文件存放路径
	 * 
	 * 二、进件
	 * </pre>
	 * @author gzh
	 * @date 2017年12月21日下午12:03:43
	 */
	public void doIntoPiece() {
		
		String savePath = getServletContext().getRealPath("/photo/intoPiece"); // 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
		String tempPath = getServletContext().getRealPath("/photo/intoPieceTemp"); 		// 上传时生成的临时文件保存目录
		File tmpFile = new File(tempPath);
		if (!tmpFile.exists()) {
			tmpFile.mkdir();
		}
		
		try {
			DiskFileItemFactory factory = new DiskFileItemFactory();
			factory.setSizeThreshold(1024 * 100);
			factory.setRepository(tmpFile);
			ServletFileUpload upload = new ServletFileUpload(factory);
//			upload.setProgressListener(new ProgressListener() {
//				public void update(long pBytesRead, long pContentLength,
//						int arg2) {
//					System.out.println("文件大小为:" + pContentLength + ",当前已处理:"
//							+ pBytesRead);
//					/**
//					 * 文件大小为:14608,当前已处理:4096 文件大小为:14608,当前已处理:7367
//					 * 文件大小为:14608,当前已处理:11419 文件大小为:14608,当前已处理:14608
//					 */
//					float f = pBytesRead / pContentLength;
//					try {
//						getHttpResponse().getWriter().write(f + "");
//					} catch (IOException e) {
//						// Auto-generated catch block
//						e.printStackTrace();
//					}
//
//				}
//			});
			upload.setHeaderEncoding("UTF-8");

			if (!ServletFileUpload.isMultipartContent(getHttpRequest())) {  // 判断提交上来的数据是否是上传表单的数据
				return;
			}

			upload.setFileSizeMax(1024 * 1024 * 5);
			upload.setSizeMax(1024 * 1024 * 5 * 5);
			List<FileItem> list = upload.parseRequest(getHttpRequest());
			for (FileItem item : list) {
				if (item.isFormField()) { // 如果fileitem中封装的是普通输入项的数据
					String name = item.getFieldName();
					String value = item.getString("UTF-8");
					log.info(name + "=" + value);
				} else { // 如果fileitem中封装的是上传文件
					String filename = item.getName();
					if (filename == null || filename.trim().equals("")) {
						continue;
					}
					// 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:
					// c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
					// 处理获取到的上传文件的文件名的路径部分,只保留文件名部分
					filename = filename
							.substring(filename.lastIndexOf("\\") + 1);
					String fileExtName = filename.substring(filename
							.lastIndexOf(".") + 1); // 得到上传文件的扩展名
					log.info("上传的文件的扩展名是:" + fileExtName); // 如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
					//读文件流数据
					InputStream in = item.getInputStream(); // 获取item中的上传文件的输入流
					String saveFilename = makeFileName(filename); 					// 得到文件保存的名称		
					String realSavePath = makePath(saveFilename, savePath); // 得到文件的保存目录
					log.info(realSavePath);
					
					FileOutputStream out = new FileOutputStream(realSavePath
							+ "\\" + saveFilename); // 创建一个文件输出流
					byte buffer[] = new byte[1024];	
					int len = 0; // 判断输入流中的数据是否已经读完的标识
					while ((len = in.read(buffer)) > 0) { // 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
						//写数据到本地磁盘
						out.write(buffer, 0, len); // 使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\"+ filename)当中
					}
					
					in.close();		
					out.close();
					item.delete();
				}
			}
			
			// TODO 进件
			
		} catch (FileUploadBase.FileSizeLimitExceededException e) {
			e.printStackTrace();
			return;
		} catch (FileUploadBase.SizeLimitExceededException e) {
			e.printStackTrace();
			return;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @Method: makeFileName
	 * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
	 * @Anthor:孤傲苍狼
	 * @param filename
	 *            文件的原始名称
	 * @return uuid+"_"+文件的原始名称
	 */
	private String makeFileName(String filename) { // 2.jpg
		// 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
		return UUID.randomUUID().toString() + "_" + filename;
	}

	/**
	 * 为防止一个目录下面出现太多文件,要使用hash算法打散存储
	 * 
	 * @Method: makePath
	 * @Description:
	 * @Anthor:孤傲苍狼
	 * 
	 * @param filename
	 *            文件名,要根据文件名生成存储目录
	 * @param savePath
	 *            文件存储路径
	 * @return 新的存储目录
	 */
	private String makePath(String filename, String savePath) {
		// 得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
		int hashcode = filename.hashCode();
		int dir1 = hashcode & 0xf; // 0--15
		int dir2 = (hashcode & 0xf0) >> 4; // 0-15
		// 构造新的保存目录
		String dir = savePath + "\\" + dir1 + "\\" + dir2; // upload\2\3
															// upload\3\5
		// File既可以代表文件也可以代表目录
		File file = new File(dir);
		// 如果目录不存在
		if (!file.exists()) {
			// 创建目录
			file.mkdirs();
		}
		return dir;
	}
}

参考

http://www.cnblogs.com/greatverve/archive/2011/12/23/android-upload.html