项目简介
图片服务器:解决项目中插入图片的问题
实现功能
核心就是一个 HTTP 服务器, 提供对图片的增删改查能力,同时搭配简单的页面辅助完成图片上传和展示
应用知识点
Web项目的设计能力,Servlet的使用
MySQL数据库的设计和使用
基于 md5 实现相同图片内容只存一份
基于白名单方式的防盗链
项目的搭建
数据库设计
drop database if exists image_system;
create database image_system character set utf8mb4;
use image_system;
drop table if exists `image_table`;
create table `image_table`(image_id int not null primary key auto_increment,
image_name varchar(50),
size bigint,
upload_time varchar(50),
md5 varchar(128),
content_type varchar(50) comment '图片类型',
path varchar(1024) comment '图片所在路径')
实现后端
创建ImageServlet类
这个类的 doPost 对应插入图片, doGet 对应查看图片信息, doDelete 对应删除图片
插入图片
getImages() {
$.ajax({
url: "image",
type: "get",
context: this,
success: function(data, status) {
this.images = data;
$("#app").resize();
}
})
}
根据前端代码,新增接口url为image,重写的是doPost方法
1.解析请求数据,获取图片的每个属性
2.根据md5校验码,验证是否上传过该图片
//1. 解析请求数据(part类接收)
//因为前端的数据类型时multipart/form-data(文件上传,上传的文件可以是任意的类型) 所以不能用之前的接收方式来接受前端给到的数据了,只能用getPart的方式接收前端的数据
Part p = req.getPart("uploadImage");
long size = p.getSize();//获取上传的文件大小
String contentType = p.getContentType();//获取每个part的数据格式
String name = p.getSubmittedFileName();//获取上传的文件
//图片上传时间,数据库是保存的字符串 用日期格式化的类来转换
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String uploadTime = df.format(date);
//获取part(上传图片文件)的输入流
InputStream is = p.getInputStream();//获取上传文件的输入流(数据)
//根据输入流转换为md5校验码
String md5 = DigestUtils.md5Hex(is);
//如何判断 图片已上传 若相同的MD5值 就不能插入数据和保存本
int num = ImageDao.queryCount(md5);
if (num>= 1){
throw new AppException("上传图片重复");
}
注意:
3,将上传图片把保存至本地路径,将图片信息保存至数据库
//2-1: 保存上传图片为服务端本地文件
//上传的图片名可能重复,但Md5是唯一的
p.write(IMAGE_DIR+"/"+md5); //p.write("E://TMP");//保存文件到服务器本地某个路径
//2-2: 图片信息保存在数据库---->后续查询图片列表接口要用
//插入数据操作,字段太多,最好把字段转换为对象的属性
Image image = new Image
image.setImageName(name);
image.setContentType(contentType);
image.setSize(size);
image.setUpload_time(uploadTime);
image.setMd5(md5);
image.setPath("/"+md5);
int n = ImageDao.insert(image);
查看图片信息
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
//1.解析请求数据
String id = req.getParameter("imageId");
Object o= null;
if ( id == null){
//查询所有图片 o= list<image>
//List<Image> list = ImageDao.queryALL();
o = ImageDao.queryALL();
}else {
//查询指定ID的一个图片 o = image对象
//Image image = ImageDao.queryOne(Integer.parseInt(id));
o = ImageDao.queryOne(Integer.parseInt(id));
}
//把对象 序列化为字符串
String json = util.serialize(o);
resp.getWriter().println(json);
删除图片
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
String id = req.getParameter("imageId");
//数据库根据ID删除图片数据
Image image = ImageDao.queryOne(Integer.parseInt(id));
int n = ImageDao.delete(Integer.parseInt(id));
//本地硬盘删除图片文件
String path = IMAGE_DIR+image.getPath();
File f = new File(path);
f.delete();
ok(resp);
创建imageshow类
获取图片内容
//1.解析请求数据 imageId
String id = req.getParameter("imageId");
//2.业务处理 2.1根据ID查询图片path字段 2.2通过path找本地图片
Image image = ImageDao.queryOne(Integer.parseInt(id));
//图片是以二进制数据放在body 同时要指定connectType
resp.setContentType(image.getContentType());
//本地图片的绝对路径
String path = ImageServlet.IMAGE_DIR + image.getPath();
//io输入流读文件
FileInputStream fis = new FileInputStream(path);
//3.返回响应 服务器本地图片的二进制数据
OutputStream os = resp.getOutputStream();
//输出流都是输出到body
byte[] bytes = new byte[1024*8];
int len;
while ((len=fis.read(bytes)) !=-1){
os.write(bytes,0,len);
}
os.flush(); //刷新缓冲区 输入流和输出流是先存储在缓冲区里面的
fis.close();
os.close();
}
基于白名单方式的防盗链
通过 HTTP 中的 refer 字段判定是否是指定网站请求图片
1.先构建一个hashset的白名单
private static final Set<String> whiteList = new HashSet<>();
static {
//白名单允许获取图片内容
whiteList.add("http://localhost:8083/java_image_server/index.html");
whiteList.add("http://localhost:8083/java_image_server/");
}
2.在方法中进行修改
String referer = req.getHeader("Referer");
if (!whiteList.contains(referer)){
//白名单里面不包含当前请求的Referer 不允许访问 返回一个403(登录了没有身份权限)401(没有登录,没有访问权限)
resp.setStatus(403);
return;//后续代码不执行了
}