mysql图片保存和读取

来源:互联网 发布:程序员出差多吗 编辑:程序博客网 时间:2024/05/29 17:23

人事信息管理系统中,需要管理用户的个人身份照片。通常这种格式的照片只有几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("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=””> 引用这个图片。还有一个小地方需要修改,因为限定上传的是身份照片,需要限定一个尺寸上限,这个在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("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">


< p>
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 鼻子痒流鼻涕流眼泪怎么办 天冷鼻子流鼻涕怎么办 感冒流水样鼻涕怎么办 一直有清水鼻涕怎么办 孩子总打喷嚏鼻塞怎么办 4岁儿童流鼻涕怎么办 孕妇清鼻涕不停怎么办 感冒流鼻水怎么办速效办法 一岁宝宝流清涕怎么办 咳嗽喉咙痛有痰怎么办 冻感冒了流鼻涕怎么办 吸烟经常嗓子疼怎么办 擦鼻涕擦破了怎么办 感冒鼻水流不停怎么办 流鼻涕鼻子都擦红了怎么办 鼻子不停的流水怎么办 擦鼻涕耳朵疼怎么办 鼻涕跟水一样怎么办 鼻涕水多打喷嚏怎么办 买来的鼻涕泥是水怎么办? 鼻炎鼻涕跟水怎么办 出门忘记带钥匙怎么办 总留鼻涕水怎么办 流像水一样的鼻涕怎么办 出门忘记带洗面奶怎么办 一侧鼻子流清水怎么办 慢性肠胃炎犯了怎么办 儿子拉肚肚子疼怎么办 皮肤太容易过敏怎么办 过敏脸一直不好怎么办 脸上突然过敏怎么办急救 脸过敏发红疼怎么办 脸上突然过敏了怎么办 脸过敏总反复怎么办 用蜂蜜洗脸过敏怎么办 脸上涂蜂蜜过敏怎么办 孕晚期脸过敏怎么办 吃芒果脸过敏怎么办 脸上老反复过敏怎么办 用什么都过敏怎么办 脸上起过敏湿疹怎么办