人事信息管理系统中,需要管理用户的个人身份照片。通常这种格式的照片只有几 K 到几十 K 大小,保存在数据库中易于进行管理和维护(如果放在文件夹下容易发生误操作而引起数据被修改或丢失)。

功能设计: 给用户提供一个上传的界面,并设定上传文件的尺寸上限。用户上传的照片先统一保存在一个临时文件夹中,之后可以用 <img> 指向临时文件夹中的这个图片,让用户可以预览自己上传的照片。当所有的用户信息都收集完成后,将图片和其他信息一并提交,保存到数据库中。保存成功以后,删除临时文件夹中的图片。

实现步骤:

我使用的是从 struts 主页上下载的 struts-1.2.8-src ,其中 web/examples/ 目录下有一个 upload 的例子,稍微修改了一下就直接拿过来用了。这是一个 JSP 页面、 ActionForm 和 Action 的组合。下面分别列出各自的代码。

upload.jsp 的部分源代码:

<html:form action="/UploadSubmit" enctype="multipart/form-data">

请选择需要上传的照片 :

<html:file property="theFile"/>

<html:submit value=" 上传 "/>

</html:form>

接下来需要在 ActionForm 中声明这个属性,并设置 getter 和 setter 方法,这部分源代码如下:

public class UploadForm extends ActionForm {

protected FormFile theFile;

public FormFile getTheFile() {

return theFile;

}

public void setTheFile(FormFile theFile) {

this.theFile = theFile;

}

}

这个表单的 theFile 属性不是 String 或 boolean ,而是 org.apache.struts.upload.FormFile 。因为用户上传的是一个二进制文件,而 HTTP 协议是以文本形式传输数据的,这就需要进行转换。打个比方,一辆汽车需要从甲地送到乙地,但是两地之间只有一条索道,汽车没法开,所以就想个办法在甲地把汽车先拆了,把零件送到乙地再重新组装成一辆汽车。 FormFile 起的就是拆卸和组装的作用,只不过它把拆卸、传输和组装的过程都封装起来了,我们看到的是一辆汽车从甲地开进 FormFile ,过一会它就从乙地开出来了 J 我们要决定的只是把它停到什么地方,这就是 Action 的活了。

按照功能设计, Action 要把这部车停到一个临时文件夹下面,这部分源代码如下:

public ActionForward execute(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

throws Exception {

if (form instanceof UploadForm) {

UploadForm theForm = (UploadForm) form;

// 获取上传的数据文件

FormFile file = theForm.getTheFile();

// 获取文件名

String filename= file.getFileName();

// 设置图片文件临时存放的路径

HttpSession session = request.getSession();

String path = session.getServletContext().getRealPath("/") + "temp//" + filename;

try {

// 读取文件中的数据,获取二进制的数据流

InputStream stream = file.getInputStream();

// 把数据写到指定路径

OutputStream bos = new FileOutputStream(path);

int bytesRead = 0;

byte[] buffer = new byte[8192];

while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {

bos.write(buffer, 0, bytesRead);

}

bos.close();

logger.info("The file has been written to /""

+ path + "/"");

// 设计一个标记,说明用户已经上传过照片了。

session.setAttribute("p_w_picpathuploaded","true");

session.setAttribute("filename",filename);

// close the stream

stream.close();

bos.flush();

bos.close();

}catch (FileNotFoundException fnfe) {

return null;

}catch (IOException ioe) {

return null;

}

//destroy the temporary file created

file.destroy();

// 转向下一个页面

return mapping.findForward("next");

}

//this shouldn't happen in this example

return null;

}

这样图片就被放在 temp 的临时文件夹下,显示的时候,只需要先检查一下标记,看看用户是否上传了照片,如果已经上传,就用一个 <img src=””> 引用这个图片。还有一个小地方需要修改,因为限定上传的是身份照片,需要限定一个尺寸上限,这个在 struts 的 upload 里面有现成的例子。先在 struts-config.xml 中配置这个 ActionForm和 Action :

<form-bean name="uploadForm"

type="org.apache.struts.webapp.upload.UploadForm"/>

……

<action input="/pages/hr/error.jsp" name="uploadForm"

path="/UploadSubmit" scope="request"

type="org.apache.struts.webapp.upload.UploadAction" validate="true">

<forward name="next" path="/pages/hr/input.jsp"/>

</action>

……

<controller maxFileSize="2M" inputForward="true" />

在配置文件中已经看到 <action> 的 validate 属性被设置成“ true ”,这就是说表单提交之前先要对其内容进行验证,这里我们要验证的就是 theFile 是否超出了 controller 中设定的最大尺寸 ”2M” 。这个验证是通过ActionForm 的 validate 方法来实现的:

/**

* Check to make sure the client hasn't exceeded the maximum allowed upload size inside of this validate method.

**/

public ActionErrors validate(ActionMapping mapping,

HttpServletRequest request) {

ActionErrors errors = null;

//has the maximum length been exceeded?

Boolean maxLengthExceeded =

(Boolean) request.getAttribute(

MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED);

if ((maxLengthExceeded != null) && (maxLengthExceeded.booleanValue())) {

errors = new ActionErrors();

errors.add(

ActionMessages.GLOBAL_MESSAGE ,

new ActionMessage("maxLengthExceeded"));

errors.add(

ActionMessages.GLOBAL_MESSAGE ,

new ActionMessage("maxLengthExplanation"));

}

return errors;

}

这里我估计有个 hook 之类的东西先截获了表单(应该和 controller 有关),对 theFile 的尺寸进行校验,然后把结果保存在 request scope 中。 Validate 方法只要检查一下这个结果就可以了,如果尺寸超标,表单就不会被提交给 Action 。

以上算是完成了第一步,从用户那里拿到照片,并保存在临时文件夹当中。接下来要做的就是把照片保存到MySQL 数据库中,这个字段我用的是 MEDIUMBLOB ,因为 BLOB 最大长度是 216-1 字节,大约 64K ;MEDIUMBLOB 是 224-1 字节,约 16M ,足够用了。保存图片的主要代码如下:

/**

* 将用户的照片保存在数据表中,添加成功后,删除临时文件夹中的图片。

* @param id 用户的身份号,作为图片的标识码

* @param path 图片存放的路径。一般存放在一个临时文件夹中。

* @return

*/

public static void saveImage(int id, String path) throws SQLException{

String time = new java.util.Date().toString();

Connection conn = null;

PreparedStatement pstmt = null;

boolean flag = false;

// 获取连接

try {

conn = DbManager.getConnection();

logger.info(time + ":saveImage() 从 DbManager 数据库连接池获取一个连接。 ");

} catch (SQLException e) {

// 如果没有能够从 DbManager 获取连接,此次查询操作失败

logger.error(time + ": saveImage() 不能获取数据库连接,无法保存图片! ");

throw new SQLException(":saveImage() 不能获取数据库连接,无法保存图片! ");

}

// 执行查询

try {

pstmt = conn.prepareStatement("UPDATE hr01 SET hr01_photo=? where hr01_id=?");

FileInputStream in = new FileInputStream(path);

pstmt.setBinaryStream(1,in,in.available());

pstmt.setInt(2,id);

pstmt.executeUpdate();

pstmt.executeUpdate("COMMIT");

logger.info(" 图片 " + path + " 被添加到数据库中! ");

flag = true;

}catch(IOException e){

logger.error(" 图片 " + path + " 文件读写错误!请检查文件路径是否正确 ");

throw new SQLException(" 无法保存图片! ");

}catch (SQLException ex) {

logger.error(new java.util.Date() + "Error:Insert into table."

+ ex.getMessage());

logger.error(" 图片 " + path +" 没有被保存到数据库 .");

throw new SQLException(" 图片 " + path +" 没有被保存到数据库 .");

} finally {

try {

pstmt.close();

conn.close();

logger.info("DbHrinfo saveImage() closed the connection created at " + time);

} catch (SQLException e) {

}

}
>

// 图片添加成功以后就删除临时文件夹中的图片数据

if(flag == true){

File file = new File(path);

if(file.exists()){

file.delete();

}

}

}

需要注意的是 pstmt.executeUpdate("COMMIT"); 这行代码,最初我并没有写这行,程序能顺利执行,日志中也显示“图片××被添加到数据库中”,但是到库里一查询,什么都没有。 SQL 语句被提交,但是数据库里面没有即时的显示,估计是缓冲区的作用,它把我的 SQL 语句缓存起来而不是立即提交给数据库。后来我在 textpad 里面单独执行这段代码,发现不用“ COMMIT ” SQL 语句就立即被提交了。这个地方还没有弄清楚,以后需要继续研究。

功能上做一点小改进,用户提交了照片以后,浏览了一下觉得不满意,只要还没有最终提交数据,当然允许他重新上传一个照片。我们只需要在 <img src=””> 下面提供一个“重新提交”的链接就可以了。这个链接指向一个AlterImageAction ,它的功能就是清除 session 中用户已经上传照片的标记,并把刚才保存到临时文件夹中的照片删掉,等待用户重新上传。这部分代码如下:

public ActionForward execute(ActionMapping mapping,

ActionForm form,HttpServletRequest request,

HttpServletResponse response)

throws IOException,ServletException{

HttpSession session = request.getSession();

//1. 从临时文件夹中删除图片

String filename = (String)session.getAttribute("filename");

String path = session.getServletContext().getRealPath("/")

+ "temp//" + filename;

File file = new File(path);

if(file.exists()){

file.delete();

logger.info(" 文件 " + path + " 已经被删除 ");

}

//2. 从 session 中清除上传图片的标记

session.removeAttribute("p_w_picpathuploaded");

session.removeAttribute("filename");

return mapping.findForward("next");

}

提交和保存到此功德圆满。下次用户想要查询自己的信息的时候,因为临时文件夹中已经没有用户照片,需要从数据库中读取。用一个 ShowImageAction 来实现这个功能:

public ActionForward execute(ActionMapping mapping,

ActionForm form, HttpServletRequest request,

HttpServletResponse response)

throws IOException,ServletException{

// 需要的情况下设置数据源

if (!DbManager.hasSetDataSource()) {

javax.sql.DataSource dataSource;

try {

dataSource = getDataSource(request);

DbManager.setDataSource(dataSource);

} catch (Exception e) {

logger.error(e.getMessage());

mapping.findForward("error");

}

}

String photo_no = request.getParameter("photo_no");

Connection conn = null;

Statement stmt = null;

// 获取连接

try {

conn = DbManager.getConnection();

logger.info("showp_w_picpath.jsp 从 DbManager 数据库连接池获取一个连接。 ");

} catch (SQLException e) {

// 如果没有能够从 DbManager 获取连接,此次查询操作失败

logger.error(" showp_w_picpath.jsp 不能获取数据库连接,无法读取图片! ");

}

try {

// 准备语句执行对象

stmt = conn.createStatement();

String sql = " SELECT hr01_photo FROM hr01 WHERE hr01_id='" + photo_no + "'";

ResultSet rs = stmt.executeQuery(sql);

if (rs.next()) {

InputStream in = rs.getBinaryStream("hr01_photo");

int bytesRead = 0;

byte[] buffer = new byte[8192];

response.setContentType("p_w_picpath/jpeg");

response.setContentLength(in.available());

OutputStream outs = response.getOutputStream();

while ((bytesRead = in.read(buffer, 0, 8192)) != -1) {

outs.write(buffer, 0, bytesRead);

}

outs.flush();

in.close();

rs.close();

} else {

rs.close();

response.sendRedirect("error.jsp");

}

}catch(SQLException e){

}finally {

try{

stmt.close();

conn.close();

}catch(SQLException ex){

}

}

return null;

}

以前一直不清楚 execute 方法中的 response 参数的用法,因为处理完以后总要 redirect 到另外一个页面,所以用的最多的就是把数据保存在 session 中,在另一个页面里再取出来用。这次纯粹是试验性地在 response 中写入 p_w_picpath/jpeg 内容,然后返回一个 null 值。最后的执行结果跟我预期的一样,在浏览器中直接显示从数据库中读出的图片。那么接下来就很好做了,只需要在 JSP 页面中设置一个 <p_w_picpath> 标签,指向这个 Action 就可以,当然,在这之前需要在 struts-config.xml 中先部署这个 Action :

<action path="/ShowImage"

type="software.action.ShowImageAction"></action>

然后在 JSP 页面中引用这个 Action 来显示图像 :

<img src="/tibet/ShowImage.do?photo_no=666542">

转载于:https://blog.51cto.com/qyweiyy/1696867

mysql图片保存和读取相关推荐

  1. SpringBoot图片保存与读取

    参考大佬博客的链接:https://www.cnblogs.com/zimug/p/13474233.html SpringBoot版本:2.3.7.RELEASE 发布时间:2022年3月 介绍: ...

  2. python图片保存_python读取和保存图片5种方法对比

    python读取和保存图片5种方法对比 python中对象之间的赋值是按引用传递的,如果需要拷贝对象,需要用到标准库中的copy模块 方法一:利用 PIL 中的 Image 函数 这个函数读取出来不是 ...

  3. delphi mysql 图片_如何读取delphi数据库中的图片

    展开全部 第7章 数据库处理实例 实例122 在数据库中存取图像 本实例演示如何在数据库中存取图像文件. 向窗体上添加一个TListBox组件.3231313335323631343130323136 ...

  4. java将图片保存进mysql_Java存储图片到Mysql

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 [1]视图层 action="${ctx}/web/UserInforServlet?method=userInforServlet" ...

  5. 图片保存到数据库和从数据库读取图片并显示(C#)

    图片保存到数据库的方法: public void imgToDB(string sql) {   //参数sql中要求保存的imge变量名称为@images //调用方法如:imgToDB(" ...

  6. python如何读取tfrecord_tensorflow将图片保存为tfrecord和tfrecord的读取方式

    tensorflow官方提供了3种方法来读取数据: 预加载数据(preloaded data):在TensorFlow图中定义常量或变量来保存所有的数据,适用于数据量不太大的情况.填充数据(feedi ...

  7. Android 10开发之 保存、读取图片

    Android 10开发之 保存.读取图片 概述 从Android 10(Q)开始,谷歌就开始修改了外部存储权限,叫做分区存储,分区存储可以分为两个目录,分别是 沙盒目录(App-specific d ...

  8. mysql保存和读取微信头像

    mysql 保存和读取微信头像 时间:2019年5月5日11:48:44 Util代码: import lombok.extern.slf4j.Slf4j; import javax.sql.rows ...

  9. 图片的基本读取和保存操作

    图片的基本读取和保存操作 编程要求 代码 相关知识 使用OpenCV,需要先进行导包操作 读取图像 保存图像 兴趣窗口 编程要求 根据提示,在右侧编辑器补充 Begin-End 代码,实现图片的读取. ...

最新文章

  1. python 编程实例 1
  2. 【错误记录】FFmpeg 推流报错 ( FLV does not support sample rate 8000, choose from (44100, 22050, 11025) )
  3. Http压测工具wrk使用指南
  4. NTU 课程笔记 CV6422 Statistical Methods Applications (1) 基本统计知识
  5. 2003 SERVER 本地连接 TCP/IP问题[转]
  6. ASP.NET Core中显示自定义错误页面-增强版
  7. IntelliJ IDEA部署javaweb项目
  8. [转]C#与数据结构--树论--平衡二叉树(AVL Tree)
  9. 什么是 SAP Spartacus UI 的 code deprecation
  10. openshift_Openshift源中的高可用性Drools无状态服务
  11. scala特性_Scala | 特性应用
  12. 如何从Alfresco中提取Language Pack
  13. 网页读不出php语句,php - phpmyadmin显示代码而不是网页 - 堆栈内存溢出
  14. (04)VTK移动模型,判断是否相交
  15. Java代码实现“爱心”表白
  16. vim command line操作
  17. ensp华为防火墙及应用
  18. 基于iOS11的HEVC(H.265)硬编码/硬解码功能开发指南
  19. Ubuntu系统下基本配置Edison
  20. Java 查询企业基本信息接口实现(企查查)

热门文章

  1. 2021年,产品经理是否仍在招聘风口?多年火热是否只是泡沫?
  2. Java常见面试题:常用 GC 调优策略有哪些?
  3. Java基础篇:如何使用 break 退出循环
  4. 还为重复安装开发环境而烦吗? 这或许是更好的解决方案 —— docker
  5. 网络运维常见交换机故障
  6. 利用人工智能“解锁”世界音乐
  7. 沃尔玛实验室 —— 为什么我们要启动开源计划
  8. Android批量图片加载经典系列——使用LruCache、AsyncTask缓存并异步加载图片
  9. URAL 1992 CVS 可持久化链栈
  10. Centos6.3 PHP编译安装JSON模块报错解决