关于mysql存取图片的三种方式(含源码示例)

最近在做小程序的后台,要求将小程序用户以upload方法上传的图片保存到mysql数据库中。
然后机缘巧合三种方式都试了,所以专门整理出来分享给大家。可能有的地方说的不太对,欢迎大家帮纠正。

================================================================================

Method1:采用BLOB数据格式存图片。

其实这种方式很不合理,数据库大小会激增会导致简单的查询都及其缓慢。

Method2:采用文本格式存储图片。

虽然也不怎么合理,因为关系型数据库本身就不太适合存巨长的大数据量的东西。
但是由于只涉及到base64加密和解码,且可以都写入后台部分,减轻前端负担。

Method3:将图片单独存放在某个服务器上,数据库只存放他们的url地址。

最高效也是最常用的方法。
后面有展示两种示例。

================================================================================

详细代码示例

================================================================================

Method1详细代码示例:

由于目前做的这个项目,同学A之前一直使用的这种方式将文件中的图片读取到数据库表,所以我只写了对BloB类型图片的取数据部分的代码。且过程较繁琐,可用性不强,就不贴了。

这个代码是A给我发的,实在太久了,她也忘了出处了。有人知道请艾特我一下,我标上链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package org.springboot.wechatcity.utill;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * 存入和读取Blob类型的JDBC数据
 */
public class BlobUtill {

    public void getBlob() {//读取Blob数据
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = JDBCTools.getConnection();
            String sql = "SELECT id,name,age,picture FROM animal WHERE id=5";
            ps = con.prepareStatement(sql);
            rs = ps.executeQuery();
            if (rs.next()) {
                int id = rs.getInt(1);
                String name = rs.getString(2);
                int age = rs.getInt(3);

                Blob picture = rs.getBlob(4);//得到Blob对象
                //开始读入文件
                InputStream in = picture.getBinaryStream();
                OutputStream out = new FileOutputStream("cat.png");
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = in.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void insertBlob() {//插入Blob
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = JDBCTools.getConnection();
            String sql = "INSERT INTO animal(name,age,picture) VALUES(?,?,?)";
            ps = con.prepareStatement(sql);
            ps.setString(1, "TheCat");
            ps.setInt(2, 8);
            InputStream in = new FileInputStream("J:/test1/TomCat.png");//生成被插入文件的节点流
            //设置Blob
            ps.setBlob(3, in);

            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCTools.release(con, ps);
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package org.springboot.wechatcity.utill;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC工具类  用来建立连接和释放连接
 */
public class JDBCTools {

    public static Connection getConnection() throws Exception {//连接数据库
        String driverClass = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://IP号:端口号/hmCity?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
        String user = "";
        String password = "";

        Properties properties = new Properties();

        InputStream in = Review.class.getClassLoader().getResourceAsStream("jdbc.properties");
        properties.load(in);

        driverClass = properties.getProperty("driver");
        url = properties.getProperty("jdbcurl");
        user = properties.getProperty("user");
        password = properties.getProperty("password");
        Class.forName(driverClass);
        return DriverManager.getConnection(url, user, password);
    }

    public static void release(Connection con, Statement state) {//关闭数据库连接
        if (state != null) {
            try {
                state.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    public static void release(ResultSet rs, Connection con, Statement state) {//关闭数据库连接
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (state != null) {
            try {
                state.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

================================================================================

Method2 详细代码示例:包括存和取的代码

示例:前端以表单形式提交数据信息和图片,后台以MultipartFile类型接收图片,并对图片进行BASE64编码,存储在mysql数据库中。

1.BASE64存图片。
note:建议图片处理部分单独写在service层,比较符合分层规则。

1
2
3
4
5
6
//头部信息
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 /**
     * TODO 将用户上传的信息存入数据库中
     * 图片以MultipartFile格式上传
     * @return
     */
    @CrossOrigin(origins = {"*", "3600"})  //跨域注解,所有域名都可访问,且cookie的有效期为3600秒
    @RequestMapping(value = "/pushMessageParam", method = RequestMethod.POST)
    public int pushMessageBody(@RequestParam String id, MultipartFile file1, MultipartFile file2, MultipartFile file3) throws IOException{//若参数为map或json格式,必须写@RequestBody

        List<MultipartFile> files =new ArrayList<>();//保存用户上传的所有图片,最多三张。
        files.add(file1);
        files.add(file2);
        files.add(file3);

//****给上传的所有jpg、jpeg格式的图片添加头部header(这样取得时候不用解码,直接拿值就行),并进行转码。****
        BASE64Encoder base64Encoder = new BASE64Encoder();//BASE64de 解码工具
           
                    try {
           
                        List<String> base64EncoderImgs = new ArrayList<>();//存放转码后的图片
                        String header = "";//为转码后的图片添加头部信息
           
                        for (int i = 0; i < files.size(); i++) {//遍历所有文件
                            if (files.get(i) != null) {
                                if (!files.get(i).getOriginalFilename().endsWith(".jpg") && !files.get(i).getOriginalFilename().endsWith(".jpeg")) {
                                    System.out.println("文件格式非法!");
                                } else if ("jpg".equals(files.get(i).getOriginalFilename())) {//files.get(i).getOriginalFilename() 获取文件的扩展名.jpg .jpeg
                                    header = "data:image/jpg;base64,";
                                } else if ("jpeg".equals(files.get(i).getOriginalFilename())) {
                                    header = "data:image/jpeg;base64,";
                                }
                                base64EncoderImgs.add(header + base64Encoder.encode(files.get(i).getBytes()));//转码
                            } else {
                                base64EncoderImgs.add(null);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }


        subMessageService.saveSubMessage(new SubMessage(id, base64EncoderImgs.get(0),
                base64EncoderImgs.get(1), base64EncoderImgs.get(2));
        System.out.println("用户消息已存入数据库!");

        return 0;
    }

2.BASE64取图片及前端显示测试

1
2
3
4
5
6
7
8
    //直接取值返给前端就行
    @RequestMapping(value = "/getCrowdInfoById", method = RequestMethod.GET)
    public String getCrowdInfoById() {
        CrowdInfo crowdInfo = new CrowdInfo();
        crowdInfo.setId(3);

        return crowdInfoService.getCrowdInfoById(crowdInfo).getPicBase64();//直接返回前端base64编码后的图片
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<!--<script src="base64.js"></script>-->
<body>

<img src="" width="350px" height="500px" id="kk">

<!--测试base64取图片的方式-->
<script src="//i2.wp.com/code.jquery.com/jquery-3.1.1.min.js"></script>
<script type="text/javascript">
    function getCode() {
        $.ajax({
            url: '/getCrowdInfoById',
            type: 'get',
            dataType:'text',
            data: {},
            success: function (result) {
                document.getElementById("kk").setAttribute("src", result);
            }
        })
    }

    window.onload = function () {
        getCode();
    }

</script>
</body>
</html>

result如下:
在这里插入图片描述

================================================================================

Method3 详细代码示例:

示例1:前端以form表单上传图片时,可以采取以下这种方法存储。

本代码由博友“园园的铁粉QAQ”提供,在此感谢这位朋友的无私奉献。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@RequestMapping(value = "/upDrugImg.htm", method = RequestMethod.POST)
    public ModelAndView upDrugImg(@RequestParam(value = "imgFile", required = false) MultipartFile file, HttpServletRequest request) {
    //file是imgFile的别名,只能上传一张图  
        String path = request.getSession().getServletContext().getRealPath("drugIMG");
        String fileName = file.getOriginalFilename();
        // 获取上传文件类型的扩展名,先得到.的位置,再截取从.的下一个位置到文件的最后,最后得到扩展名
        String ext = fileName.substring(fileName.lastIndexOf(".") + 1,fileName.length());
        // 对扩展名进行小写转换
        ext = ext.toLowerCase();
        // 定义一个数组,用于保存可上传的文件类型
        List fileTypes = new ArrayList();
        fileTypes.add("jpg");
        fileTypes.add("jpeg");
        fileTypes.add("bmp");
        fileTypes.add("gif");
        if (!fileTypes.contains(ext)) { // 如果扩展名属于允许上传的类型,则创建文件
            System.out.println("文件类型不允许");
            return new ModelAndView("errorpage/404");
        }
        // String fileName = new Date().getTime()+".jpg";
        File targetFile = new File(path, fileName);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        // 保存
        try {
            file.transferTo(targetFile);
        } catch (Exception e) {
            // e.printStackTrace();
            return new ModelAndView("errorpage/500");
        }
       
//******************这部分根据自己需求写******************
        //将图片名存入数据库
        String drugImg = "/drugIMG/" + fileName;
        Drug drug = (Drug) request.getSession().getAttribute("currentDrug");
        drug.setDrug_picture(drugImg);
        int flag = drugService.upDrugImg(drug);
        if (flag != 1) {
            // System.out.println("info:upload image failed!");
            return new ModelAndView("redirect:./goUpDrugImg.htm");
        }
        return new ModelAndView("redirect:./goAllDrugByHouse.htm", "updown", "down");
    }

前端jsp页面及后台实体类。
在这里插入图片描述

示例2:小程序前端以upload()方式上传图片,后台接收将图片存储到服务器,并随机生成不重复的图片名,最后将图片名存入mysql数据库。

1.SpringContextUtil工具类,直接copy就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package org.springboot.wechatcity.utill;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * Spring工具类
 * 在非spring生命周期的地方使用javabean
 * @author _Yuan
 */
@SuppressWarnings("unchecked")
@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext appContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        appContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return appContext;
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name, Class<T> clazz) throws BeansException {
        return (T) appContext.getBean(name);
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

}

2.上传图片类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.io.File;
import javax.servlet.http.HttpServlet;


/**
 * 图片上传到服务器,并将图片名存入数据库
 *
 * @author _Yuan
 * @since 2020年6月10日00:14:45
 */

@CrossOrigin(origins = {"*", "3600"})  //跨域注解,所有域名都可访问,且cookie的有效期为3600秒
@WebServlet(name = "firstServlet", urlPatterns = "/uploadPicture")  //标记为servlet,以便启动器扫描。
public class UploadPictureController extends HttpServlet {

    private static final Logger logger = LoggerFactory.getLogger(UploadPictureController.class);//日志
   
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        Map<String, Object> resultMap = new HashMap<>();//存返回信息,建议写,较规范

        //获取文件需要上传到的路径
        @SuppressWarnings("deprecation")
        String path = request.getRealPath("/upload") + "/";

        // 判断存放上传文件的目录是否存在(不存在则创建)
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdir();
        }
        logger.debug("path=" + path);

        try {
            //使用Apache文件上传组件处理文件上传步骤:
            //1、创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //2、创建一个文件上传解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //3、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
            List<FileItem> list = upload.parseRequest(request);

//****看需求,我是一次只能传一张图,其实可以不用写成List***
            List<String> names = new ArrayList<>();//用于存放所有图片名

            for (FileItem item : list) {
                //如果fileitem中封装的是普通输入项的数据
                if (item.isFormField()) {
                    String name = item.getFieldName();
                    //解决普通输入项的数据的中文乱码问题
                    String value = item.getString("UTF-8");
                    System.out.println(name + "=" + value);
                } else {//如果fileitem中封装的是上传文件
                    //得到上传的文件名称,
                    String uuid = UUID.randomUUID().toString().replace("-", "");//UUID生成不重复的一串数字
                    String filename = uuid + "." + "jpg";
                    names.add(filename);
                    System.out.println("文件名:" + filename);

                    //获取item中的上传文件的输入流
                    InputStream in = item.getInputStream();
                    //创建一个文件输出流
                    FileOutputStream out = new FileOutputStream(path + "\" + filename);
                    //创建一个缓冲区
                    byte buffer[] = new byte[1024];
                    //判断输入流中的数据是否已经读完的标识
                    int len = 0;
                    //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
                    while ((len = in.read(buffer)) > 0) {
                        //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\" + filename)当中
                        out.write(buffer, 0, len);
                    }
                    //关闭输入流
                    in.close();
                    //关闭输出流
                    out.close();
                    //删除处理文件上传时生成的临时文件
                    item.delete();
                    System.out.println("文件上传服务器成功!");
                }
            }
           
           
            //上传所有文件名
            System.out.println("图片名正在上传...请稍等");
            //******非spring生命周期用注解需要用到SpringContextUtil工具类*****
            SubMessageService subMessageService = SpringContextUtil.getBean("sub", SubMessageService.class);

            GetInfoId id = new GetInfoId();//为了获取ID,专门写的类
            int ID = id.getID();
            if (names.size() != 0) {//上传了图片
                try {
                    subMessageService.uploadAllPictureNames(new PicturesNames(ID, names.get(0), null, null));//根据Id更新所有图片
                    System.out.println("消息ID为:" + ID);
                    System.out.println("图片名已上传数据库成功~");
                }catch (Exception e){
                    e.printStackTrace();
                }
            } else {
                System.out.println("未上传图片");
            }

        } catch (Exception e) {
            System.out.println("文件上传失败!");
            e.printStackTrace();

        }


        resultMap.put("code", 0);
        resultMap.put("msg", "图片上传成功");
        return resultMap;
    }

结果:
在这里插入图片描述
在这里插入图片描述

关于本示例,如果还有困惑或者有兴趣可以戳我另一篇文章。---->>>>小程序后台同时接收前端上传的数据信息和图片的示例