环境:maven+SpringMVC + Spring + MyBatis + MySql
本文主要说明如何使用input上传文件到服务器指定目录,或保存到数据库中;如何从数据库下载文件,和显示图像文件并实现缩放。
将文件存储在数据库中,一般是存文件的byte数组,对应的数据库数据类型为blob。
首先要创建数据库,此处使用MySql数据库。
注意:文中给出的代码多为节选重要片段,并不齐全。
1. 前期准备
使用maven创建一个springMVC+spring+mybatis+mysql的项目。
关于如何整合Spring+mybatis+mysql,请见MyBatis简介与配置MyBatis+Spring+MySql:
关于SpringMVC环境的搭建请见:使用Eclipse构建Maven的SpringMVC项目:
在前台html中,form的enctype为multipart/form-data。注意input、select的name要和StudentForm中成员一一对应。
上传的url为addAction.do,此action方法的参数中使用StudentForm来映射提交的数据。此时就可以获取到提交的文件的数据。然后我们就对文件进行操作。
创建PHOTO_TBL表:PHOTO_DATA字段用于存放文件,类型为MyBatis的longblob;然后写Mapper的Java接口PhotoMapper:包括增删改查;mapper的xml文件:对应JAVA接口的sql语句。
并且需要Spring配置文件添加一个bean的声明。
下面给出html、action、StudentForm的代码片段;创建PHOTO_TBL表的sql、PhotoMapper.java接口代码、PhotoMapper.xml文件代码。
<form action="<c:url value='addAction.do' />" method="post" enctype="multipart/form-data"> <table> <tr> <td width="100" align="right">照片:</td> <td><input type="file" name="studentPhoto"/></td> </tr> </table> <input type="submit"> </form>
/** * 新增 - 提交 */ @RequestMapping(value = "addAction.do") public String add_action(ModelMap model, StudentForm form) { }
package liming.student.manager.web.model; import org.springframework.web.multipart.MultipartFile; public class StudentForm extends GeneralForm { private String studentName; private int studentSex; private String studentBirthday; private MultipartFile studentPhoto; }
CREATE TABLE PHOTO_TBL ( PHOTO_ID VARCHAR(100) PRIMARY KEY, PHOTO_DATA LONGBLOB, FILE_NAME VARCHAR(10) );
@Repository @Transactional public interface PhotoMapper { public void createPhoto(PhotoEntity entity); public int deletePhotoByPhotoId(String photoId); public int updatePhotoDate(@Param("photoId") String photoId, @Param("photoDate") byte[] photoDate); public PhotoEntity getPhotoEntityByPhotoId(String photoId); }
包括增、删、改、查。其中新增中的photoId使用的是mysql自定义函数自动生成主键。在操作blob时需要制定typeHandler为"org.apache.ibatis.type.BlobTypeHandler。insert、update时参数后面需要指定,resultMap中需要指定。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="liming.student.manager.data.PhotoMapper"> <resultMap type="liming.student.manager.data.model.PhotoEntity" id="photoMapper_resultMap_photoEntity"> <id property="photoId" column="PHOTO_ID" javaType="String" jdbcType="VARCHAR" /> <result property="photoData" column="PHOTO_DATA" javaType="byte[]" jdbcType="BLOB" typeHandler="org.apache.ibatis.type.BlobTypeHandler" /> <result property="fileName" column="FILE_NAME" javaType="String" jdbcType="VARCHAR" /> </resultMap> <insert id="createPhoto" parameterType="liming.student.manager.data.model.PhotoEntity"> <selectKey keyProperty="photoId" resultType="String" order="BEFORE"> select nextval('photo') </selectKey> INSERT INTO PHOTO_TBL(PHOTO_ID, PHOTO_DATA, FILE_NAME) VALUES(#{photoId, jdbcType=VARCHAR}, #{photoData, javaType=byte[], jdbcType=BLOB, typeHandler=org.apache.ibatis.type.BlobTypeHandler}, #{fileName, jdbcType=VARCHAR}) </insert> <delete id="deletePhotoByPhotoId"> DELETE FROM PHOTO_TBL WHERE PHOTO_ID = #{photoId, jdbcType=VARCHAR} </delete> <update id="updatephotoData" > UPDATE PHOTO_TBL SET PHOTO_DATA = #{photoData, javaType=byte[], jdbcType=BLOB, typeHandler=org.apache.ibatis.type.BlobTypeHandler}, FILE_NAME = #{fileName, jdbcType=VARCHAR} WHERE PHOTO_ID = #{photoId, jdbcType=VARCHAR} </update> <select id="getPhotoEntityByPhotoId" resultMap="photoMapper_resultMap_photoEntity"> SELECT PHOTO_ID, PHOTO_DATA, FILE_NAME FROM PHOTO_TBL WHERE PHOTO_ID = #{photoId, jdbcType=VARCHAR} </select> </mapper>
需要Spring配置文件添加一个org.springframework.web.multipart.commons.CommonsMultipartResolver的bean的声明。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="1073741824" /> </bean>
private static final String uploadFilePath = "d:\\temp_upload_file\\"; /** * 新增 - 提交 – 只保存文件到服务器上 */ @RequestMapping(value = "addAction.do") public String add_action(ModelMap model, StudentForm form) { try { MultipartFile uploadFile = form.getStudentPhoto(); String filename = uploadFile.getOriginalFilename(); InputStream is = uploadFile.getInputStream(); // 如果服务器已经存在和上传文件同名的文件,则输出提示信息 File tempFile = new File(uploadFilePath + filename); if (tempFile.exists()) { boolean delResult = tempFile.delete(); System.out.println("删除已存在的文件:" + delResult); } // 开始保存文件到服务器 if (!filename.equals("")) { FileOutputStream fos = new FileOutputStream(uploadFilePath + filename); byte[] buffer = new byte[8192]; // 每次读8K字节 int count = 0; // 开始读取上传文件的字节,并将其输出到服务端的上传文件输出流中 while ((count = is.read(buffer)) > 0) { fos.write(buffer, 0, count); // 向服务端文件写入字节流 } fos.close(); // 关闭FileOutputStream对象 is.close(); // InputStream对象 } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
/** * 新增 - 提交 – 保存文件到数据库 */ @RequestMapping(value = "addAction.do") public String add_action(ModelMap model, StudentForm form) { InputStream is = form.getStudentPhoto().getInputStream(); byte[] studentPhotoData = new byte[(int) form.getStudentPhoto().getSize()]; is.read(studentPhotoData); String fileName = form.getStudentPhoto().getOriginalFilename(); PhotoEntity photoEntity = new PhotoEntity(); photoEntity.setPhotoData(studentPhotoData); photoEntity.setFileName(fileName); this.photoMapper.createPhoto(photoEntity); }
下载文件需要将byte数组还原成文件。
首先使用mybatis将数据库中的byte数组查出来,指定文件名(包括格式)。然后使用OutputStream将文件输入
@RequestMapping(value = "downPhotoById") public void downPhotoByStudentId(String id, final HttpServletResponse response){ PhotoEntity entity = this.photoMapper.getPhotoEntityByPhotoId(id); byte[] data = entity.getPhotoData(); String fileName = entity.getFileName()== null ? "照片.png" : entity.getFileName(); fileName = URLEncoder.encode(fileName, "UTF-8"); response.reset(); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); response.addHeader("Content-Length", "" + data.length); response.setContentType("application/octet-stream;charset=UTF-8"); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); outputStream.write(data); outputStream.flush(); outputStream.close(); }
<a href="<%=request.getContextPath() %>/downPhotoById.do?id=8000001">下载照片</a>
@RequestMapping(value = "getPhotoById") public void getPhotoById (String id, final HttpServletResponse response){ PhotoEntity entity = this.photoMapper.getPhotoEntityByPhotoId(id); byte[] data = entity.getPhotoData(); response.setContentType("image/jpeg"); response.setCharacterEncoding("UTF-8"); OutputStream outputSream = response.getOutputStream(); InputStream in = new ByteArrayInputStream(data); int len = 0; byte[] buf = new byte[1024]; while ((len = in.read(buf, 0, 1024)) != -1) { outputSream.write(buf, 0, len); } outputSream.close(); }
<img src="<%=request.getContextPath() %>/getPhotoById.do?id=8000001"/>
@RequestMapping(value = "getPhotoId") public void getPhotoById (String id, int width, int height, final HttpServletResponse response){ PhotoEntity entity = this.photoMapper.getPhotoEntityByPhotoId(id); byte[] data = entity.getPhotoData(); if (width != 0 && height != 0) { data = scaleImage(data, width, height); } response.setContentType("image/jpeg"); response.setCharacterEncoding("UTF-8"); OutputStream outputSream = response.getOutputStream(); InputStream in = new ByteArrayInputStream(data); int len = 0; byte[] buf = new byte[1024]; while ((len = in.read(buf, 0, 1024)) != -1) { outputSream.write(buf, 0, len); } outputSream.close(); } public static byte[] scaleImage(byte[] data, int width, int height) throws IOException { BufferedImage buffered_oldImage = ImageIO.read(new ByteArrayInputStream(data)); int imageOldWidth = buffered_oldImage.getWidth(); int imageOldHeight = buffered_oldImage.getHeight(); double scale_x = (double) width / imageOldWidth; double scale_y = (double) height / imageOldHeight; double scale_xy = Math.min(scale_x, scale_y); int imageNewWidth = (int) (imageOldWidth * scale_xy); int imageNewHeight = (int) (imageOldHeight * scale_xy); BufferedImage buffered_newImage = new BufferedImage(imageNewWidth, imageNewHeight, BufferedImage.TYPE_INT_RGB); buffered_newImage.getGraphics().drawImage(buffered_oldImage.getScaledInstance(imageNewWidth, imageNewHeight, BufferedImage.SCALE_SMOOTH), 0, 0, null); buffered_newImage.getGraphics().dispose(); ByteArrayOutputStream outPutStream = new ByteArrayOutputStream(); ImageIO.write(buffered_newImage, "jpeg", outPutStream); return outPutStream.toByteArray(); }
<img src="<%=request.getContextPath() %>/getPhotoById.do?id=8000001&width=300&height=300"/>