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

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

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

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

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

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

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

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

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

详细代码示例

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

Method1详细代码示例:

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

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

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);

}

}

}

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层,比较符合分层规则。

//头部信息

import org.springframework.web.multipart.MultipartFile;

import sun.misc.BASE64Encoder;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

/**

* 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 files =new ArrayList<>();//保存用户上传的所有图片,最多三张。

files.add(file1);

files.add(file2);

files.add(file3);

//****给上传的所有jpg、jpeg格式的图片添加头部header(这样取得时候不用解码,直接拿值就行),并进行转码。****

BASE64Encoder base64Encoder = new BASE64Encoder();//BASE64de 解码工具

try {

List 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取图片及前端显示测试

//直接取值返给前端就行

@RequestMapping(value = "/getCrowdInfoById", method = RequestMethod.GET)

public String getCrowdInfoById() {

CrowdInfo crowdInfo = new CrowdInfo();

crowdInfo.setId(3);

return crowdInfoService.getCrowdInfoById(crowdInfo).getPicBase64();//直接返回前端base64编码后的图片

}

Title

result如下:

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

Method3 详细代码示例:

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

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

@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就行

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 getBean(String name, Class 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 getBean(Class clazz){

return getApplicationContext().getBean(clazz);

}

}

2.上传图片类

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 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对应一个Form表单的输入项

List list = upload.parseRequest(request);

//****看需求,我是一次只能传一张图,其实可以不用写成List***

List 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;

}

结果:

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