最近学习了图片上传这个功能,这个功能比较常见,因此来整理一下,了解上传的基本原理,以便后期遇到图片上传功能可以很快上手。
要说图片上传,我们先来说一下图片上传后存储的两种方式:一种是将图片存储到数据库中;一种是将图片存储在服务器文件目录中。首先,对于将图片存储到数据库中适合数据量小的情况,因为写到数据库的图片需要转换成二进制流的格式,占用数据空间比较,适合少量图片的存储,比如,系统中某些小图标,写到数据库中的优点是比较安全,不容易被用户不小心删除。但是,图片存在数据库的操作方面的局限性太大,还要拼凑sql,db
server还要parse sql, write into file,读写性能不高,备份越来越大。如果是大量的图片存储通常的做法是保存到服务器的某个目录下。一方面,其完成图片上传有很多方式,可以采用流的方式,可以采用ftp的方式等;另一方面备份方便(只备DB),读取高性能。这种方式便于直接访问,适用于直接显示方面的需求,但路径与图片的映射容易出问题。对于超级大型应用,需要的是数据库的批量查询和返回结果,这时应用分布式将图片存储在数据库中,比如谷歌的Bigtable,也可以理解成nosql,以及Amazon的S3存储服务是基于文档型数据库的,也是nosql。现在很多网站直接把自己的二进制数据放在S3,能够满足全球分布式管理,并且不用自己动手。想说一句,看应用的规模,以及扩展性的需要,选择适合的图片存储方式,没有绝对的答案。
下面我将以图片存储在服务器目录中的形式,介绍一下fileupload方式的图片上传。common-fileupload组件是apache的一个开源项目之一,可以从http://jakarta.apache.org/commons/fileupload/下载。该组件简单易用,可实现一次上传一个或多个文件,并可限制文件大小。
下面我就讲一下基本实现:
首先需要在lib目录下引入:commons-io-1.3.2.jar和commons-fileupload-1.3.1.jar。
前端代码:
<span style="font-size:14px;"><%@ page language="java" import="java.util.*" pageEncoding="GB18030"%> <html> <head> <title>fileUpload</title> <meta http-equiv="Content-Type" content="text/html; charset=GB18030"> </head> <body> <form action="./servlet/FileUploadServlet" method="post" enctype="multipart/form-data" name="form1"> <input type="file" name="file"> <input type="submit" name="Submit" value="upload"> </form> <form action="./servlet/HelloWord" method="post"> <input type="submit"/> </form> <form name="uploadform" method="POST" action="./servlet/FileUploadServlet" ENCTYPE="multipart/form-data"> <table border="1" width="450" cellpadding="4" cellspacing="2" bordercolor="#9BD7FF"> <tr><td width="100%" colspan="2"> 文件1:<input name="x" size="40" type="file"> </td></tr> <tr><td width="100%" colspan="2"> 文件2:<input name="y" size="40" type="file"> </td></tr> <tr><td width="100%" colspan="2"> 文件3:<input name="z" size="40" type="file"> </td></tr> </table> <br/><br/> <table> <tr><td align="center"><input name="upload" type="submit" value="开始上传"/></td></tr> </table> </form> </body> </html> </span>
注意:文件上传在前端要使用file标签,采用form表单提交enctype=" multipart/form-data
" 。关于enctype="multipart/form-data"的说明:在jsp中使用了该格式,对应的Servlet就不能使用request.getParameter()取得参数,要使用ServletFileUpload对象的parseRequest方法先把request对象中的数据解析,然后,使用解析出的元素的isFormField标志,配合getFieldName方法来获取数据。
java代码:
<span style="font-size:14px;">package uploadPack; import java.io.File; import java.io.IOException; import java.util.Iterator; 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; @SuppressWarnings("serial") public class FileUploadServlet extends HttpServlet { //存储图片到数据库类的方法 ItemManager itemManager = new ItemManagerImpl() ; //用于存放上传文件 private File uploadPath; //用于存放临时文件的目录 private File tempPath; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //从test_upload.jsp中拿取数据,因为上传页的编码格式跟一般的不同,使用的是enctype="multipart/form-data" //form提交采用multipart/form-data,无法采用req.getParameter()取得数据 //DiskFileItemFactory:创建FileItem对象的工厂,这个工厂类中可以配置内存缓冲池大小和临时文件的目录 DiskFileItemFactory factory = new DiskFileItemFactory(); // maximum size that will be stored in memory factory.setSizeThreshold(4096); // the location for saving data that is larger than getSizeThreshold() factory.setRepository(tempPath); //ServletFileUpload:负责处理上传的文件数据,并将每部分的数据封装成到FileItem对象中 //在接收上传文件数据时,会将内容保存到内存缓存区中,如果文件内容超过了 DiskFileItemFactory 指定的缓冲区的大小, //那么文件将被保存到磁盘上,存储为DiskFileItemFactory指定目录中的临时文件 //等文件数据都接收完毕后,ServletFileUpload再从文件中将数据写入到上传文件目录下的文件中 ServletFileUpload upload = new ServletFileUpload(factory); // maximum size before a FileUploadException will be thrown upload.setSizeMax(1000000* 100); try { //从test_upload.jsp中拿取数据,因为上传页的编码格式跟一般的不同,使用的是enctype="multipart/form-data" //form提交采用multipart/form-data,无法采用req.getParameter()取得数据 List fileItems =upload.parseRequest(req); //循环提交的表单 for (Iterator iter = fileItems.iterator(); iter.hasNext();) { FileItem item = (FileItem) iter.next(); String itemNo = ""; //判断是文件还是文本信息 //是普通的表单输入域 if(item.isFormField()) { if ("itemNo".equals(item.getFieldName())) { //将FileItem对象中保存的主体内容作为一个字符串返回,乱码可以加编码方式 itemNo = item.getString(); } } //是否为input="type"输入域 if(!item.isFormField()){ //上传文件的名称和完整路径 String name =item.getName(); long size =item.getSize(); //判断是否选择了文件 if ((name ==null || name.equals("")) && size==0) { continue; } //截取字符串 name = name.substring(name.lastIndexOf("\\") + 1, name.length()); //将文件保存到目录下,不修改文件名 item.write(new File(uploadPath, name)); //将图片文件名写入打数据库 itemManager.uploadItemImage(itemNo, name); } } resp.sendRedirect(req.getContextPath() + "/servlet/item/SearchItemServlet"); } catch (Exception e) { e.printStackTrace(); throw new ApplicationException("上传失败!"); } } /** * 在系统启动的时候就初始化,在初始化时,检查上传图片的文件夹和存放临时文件的文件夹,如果不存在就创建 */ @Override public void init() throws ServletException { //获取根目录对应的真实物理路径 uploadPath = new File(getServletContext().getRealPath("/upload")); //如果目录不存在 if (!uploadPath.exists()) { //创建目录 uploadPath.mkdir(); } //临时目录 //File tempFile = new File(item.getName())构造临时对象 tempPath = new File(getServletContext().getRealPath("/temp")); if (!tempPath.exists()) { tempPath.mkdir(); } } } </span>
配置文件:
<span style="font-size:14px;"><?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>FileUploadServlet</servlet-name> <servlet-class>uploadPack.FileUploadServlet</servlet-class> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping> <servlet-name>FileUploadServlet</servlet-name> <url-pattern>/servlet/FileUploadServlet</url-pattern> </servlet-mapping> </web-app> </span>
后面还会遇到很多其他上传图片的方法,会继续总结整理。