struts+MySQL实现图片的存储与显示

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

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

实现步骤:

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

upload.jsp的部分源代码:

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

      请选择需要上传的照片:

     <html:file property="theFile"/>

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

</html:form>

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

public class UploadForm extends ActionForm {

    protected FormFile theFile;

    public FormFile getTheFile() {

        return theFile;

    }

    public void setTheFile(FormFile theFile) {

        this.theFile = theFile;

    }

}

这个表单的theFile属性不是Stringboolean,而是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("imageuploaded","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=””>引用这个图片。还有一个小地方需要修改,因为限定上传的是身份照片,需要限定一个尺寸上限,这个在strutsupload里面有现成的例子。先在struts-config.xml中配置这个ActionFormAction

<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 ”。这个验证是通过ActionFormvalidate方法来实现的:

/**

     * 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字节,大约64KMEDIUMBLOB224-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里面单独执行这段代码,发现不用“COMMITSQL语句就立即被提交了。这个地方还没有弄清楚,以后需要继续研究。

功能上做一点小改进,用户提交了照片以后,浏览了一下觉得不满意,只要还没有最终提交数据,当然允许他重新上传一个照片。我们只需要在<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("imageuploaded");

     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("showimage.jsp DbManager数据库连接池获取一个连接。");

     } catch (SQLException e) {

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

        logger.error(" showimage.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("image/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中写入image/jpeg内容,然后返回一个null值。最后的执行结果跟我预期的一样,在浏览器中直接显示从数据库中读出的图片。那么接下来就很好做了,只需要在JSP页面中设置一个<image>标签,指向这个Action就可以,当然,在这之前需要在struts-config.xml中先部署这个Action

<action path="/ShowImage"

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

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

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

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值