文件上传:
RFC1867协议的请求与响应的特点:
通过浏览器上传文件给服务器,与正常的访问不同,用户通过浏览器给服务器发送的比较复杂,而向浏览器返回的信息
可能是简单的信息,为了规范通过浏览器上传文件到服务器的行为,就设计了一个RFC1867协议,约定上传文件所需要尊守
的规则。
文件上传的本质:
从客户端复制文件到服务端指定目录下面的过程
文件上传表单三要素:
1,使用post方式提交表单
2,为上传表单的每个表单项添加name属性,便于服务器收集
3,在form标签中,加上enctype="multipart/form-data" 只有在上传文件时使用
为了便于上传,需在项目中引入:commons-fileupload-1.2.1.jar和commons-io-1.4.jar两个包
下面演示FileUpload包API的使用:
upload.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
My JSP upload page
FileUploadServlet
import itcast.util.WebUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.List;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class FileUploadServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try{
//设置上传参数编码为UTF-8
request.setCharacterEncoding("UTF-8");
//创建文件上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置临时缓存区为100k
factory.setSizeThreshold(100*1024);
//设置超过100k时,缓存文件的存放路径
factory.setRepository(new File(this.getServletContext().getRealPath("/WEB-INF/temp")));
//创建文件上传对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//判断是否以RFC协议方式上传文件
if(fileUpload.isMultipartContent(request)){
//解析request中的所有表单字段
List fileItemList = fileUpload.parseRequest(request);
for(FileItem fileItem : fileItemList){
//如果是普通字段
if(fileItem.isFormField()){
/*
* 做获取字段操作
* String name = fileItem.getFieldName();
* String value = fileItem.getString("UTF-8");
*/
}else if(!fileItem.isFormField()){
long size = fileItem.getSize();
if(size > 200*1024){
request.setAttribute("message","上传文件大
小必须小于或等于200k
继续上传浏览列表");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
continue ;
}
String contentType = fileItem.getContentType();
String agent = request.getHeader("USER-AGENT"); //获取agent参数,IE有MSIE参数,FF没有该参数
System.out.println(contentType);
if(!"image/pjpeg".equals(contentType) && !"image/jpeg".equals(contentType) &&
!"application/octet-stream".equals(contentType)){
request.setAttribute("message","上传文件类型
必须是jpg继续上传
浏览列表");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
continue ;
}
String filename = fileItem.getName();
filename = WebUtil.makeUuidFileName(filename);
//创建目录把文件分散在2级子目录下
String uploadPath = WebUtil.makeSubUploadPath(this.getServletContext().getRealPath("/WEB-INF/upload"),
filename);
//把文件写到服务器自定服务器下面
InputStream is = fileItem.getInputStream();
OutputStream os = new FileOutputStream(new File(uploadPath+"/"+filename));
WebUtil.writeToFile(is, os);
fileItem.delete();//删除缓存的临时文件
}
}
}
}catch(Exception e){
}
request.setAttribute("message","上传文件成功
继续上传浏览列表");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}
}
public static String makeUuidFileName(String filename){
return UUID.randomUUID().toString()+"_"+filename;
}
/*
* 该方法是把上传的文件打散在不同的子目录里面
*/
public static String makeSubUploadPath(String path,String name){
int code = name.hashCode();
int dir1 = Math.abs(code % 16); //第一级目录
int dir2 = Math.abs((code >> 1) % 16); //第二级目录
File file = new File(path+"/"+dir1+"/"+dir2);
if(!file.exists()){ //如果目录不存在就创建该目录
file.mkdirs();
}
return file.getPath();
}
/**
* 把输入流写到输出流里面
* @param is
* @param os
*/
public static void writeToFile(InputStream is,OutputStream os){
try{
int len = 0;
byte[] buf = new byte[2048];
while((len = is.read(buf)) > 0){
os.write(buf, 0, len);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
if(is != null){
is.close();
}
if(os != null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static String getFileName(String filename){
return filename.substring(filename.indexOf("_")+1);
}
}
上面例子中,在WEB-INF中创建了一个upload文件夹,同时在为了解决不同用户上传相同文件名的问题,使用UUID分别
为每个文件区分,同时为了在upload中的文件夹打散在子文件夹,利用了添加UUID后的UUID文件名的哈希码,得到一个子目
录路径,这样做的好处是:在下载的时候可以通过UUID文件名调用相同的方法获取子目录路径,从而获取输入流。
ListFileServlet
import itcast.util.WebUtil;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取上传文件存放路径
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
//定义一个集合将文件名存放好
Map map = new LinkedHashMap();
//递归遍历所有文件
getAllFile(uploadPath,map);
//将map保存到request中
request.setAttribute("MAP", map);
//转发到list.jsp 页面
request.getRequestDispatcher("/WEB-INF/list.jsp").forward(request, response);
}
public void getAllFile(String uploadPath, Map map) {
File file = new File(uploadPath);
if(file.isFile()){
String uuidFileName = file.getName();
//从uuidName 中截取出filename
String filename = WebUtil.getFileName(uuidFileName);
map.put(uuidFileName, filename);
}else if(file.isDirectory()){ //如果是目录的话
File[] files = file.listFiles();
for(File f : files){
getAllFile(f.getPath(), map);
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
My JSP 'list.jsp' starting page
- 文件名下载
- ${item.value}下载
FileDownloadServlet
import itcast.util.WebUtil;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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 FileDownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决中文乱码问题
//获取参数中的文件名称
String uuidFileName = request.getParameter("name");
uuidFileName = new String(uuidFileName.getBytes("ISO8859-1"),"UTF-8");
//从uuid文件名中获取文件名
String filename = WebUtil.getFileName(uuidFileName);
System.out.println(filename);
//因为是中文,要进行URL编码,该方法在Firefox Netscape 5.0 (Windows) 失效
filename = URLEncoder.encode(filename,"UTF-8");
System.out.println(filename);
//获取上传文件的根目录
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
//内部是通过uuidFileName的hasCode编码的,所以相同的文件名,可以获取相同的子路径
String path = WebUtil.makeSubUploadPath(uploadPath, uuidFileName);
//通知浏览器以下载的方式打开
response.setHeader("content-disposition","attachment;filename="+filename);
//从实际文件中读取流
InputStream is = new FileInputStream(path+"/"+uuidFileName);
OutputStream os = response.getOutputStream();
//把内容写到浏览器
WebUtil.writeToFile(is, os);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
注解是JDK5,新增的语法功能,就是用@来标识该类,方法,属性的相关信息
注解的作用:通知编译器如何处理被注解修饰过的内容,在某种情况下,可以替代原来的XML或properties等文本文件,
例如hibernate,spring,android 中会证明。
1,@Override
复写父类方法
2,@Deprecated
废弃方法,已经过时。
3,@SuppressWarnings (value="unchecked")
表示通知编译器不要警告
JDK5中的内置注解,不能瞒住企业方方面面的业务续修,所以的按规则,开发注解
自定义注解的生命周期
SOURCE:有且只有在源码级别可见,(不推荐使用)
CLASS: 有且只有在源码和字节码级别可见,(默认),但在运行时,不可见
RUNTIME:在整个类的运行期周,都可见。(即源码,字节码,运行时)
元注解的作用
修饰其它注解的注解,注意:元注解也可以被其它注解修饰
@Retention(RetentionPolicy.RUNTIME):表示该注解可以在运行时,可见
@Target(ElementType.METHOD):表示该注解可以用在哪些地方,默认情况下,该注解可以出现在任何地方
自定义注解应用示例:
DB.java
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DB {
String driver() default "com.mysql.jdbc.Driver";
String url() default "jdbc:mysql://127.0.0.1:3306/jdbc";
String user() default "root";
String password() default "root";
}
Demo03.java
public class Demo03 {
@DB
public Connection getConnection() throws Exception {
Class clazz = this.getClass();
Method getConnectionMethod = clazz.getMethod("getConnection",null);
DB dbAnnotation = getConnectionMethod.getAnnotation(DB.class);
String driver = dbAnnotation.driver();
String url = dbAnnotation.url();
String user = dbAnnotation.user();
String password = dbAnnotation.password();
Class.forName(driver);
return DriverManager.getConnection(url,user,password);
}
public static void main(String[] args) throws Exception {
Demo03 test = new Demo03();
Connection conn = test.getConnection();
System.out.println(conn!=null?"连接成功":"连接失败");
conn.close();
}
}