前面我们已经将醒目的主要框架搭建起来了,现在我们主要来解决前面遗留的问题,并且对新加入的功能进行代码编写,例如:md5实现,加入白名单实现防盗功能,按照图片类型查看图片功能
我们使用MD5将图片内容的到我们所需要的一串唯一的代表图片内容的特征码
MD5码可以唯一地代表原信息的特征,通常用于密码的加密存储,数字签名,文件完整性验证等。
我们如果想要使用md5来进行存储,那么
1.首先我们存储图片的路径应该变为图片md5值(需要计算md5值,然而计算md5值需要引入依赖)
2.其次为了避免重复存储我们需要在将文件写入磁盘前进行判断,判断该md5值的图片在数据库中是否为空(在图片属性信息存入数据库之前),如果存在不为空该md5值的图片则说明之前已经保存过相同的图片了(相同图片内容对应的md5值相同)无需再次保存
3.判断md5值相同的image是否只有一个需要一个按照md5值查找数据库操作的方法,在ImageDao中加入一个按照Md5值查找数据库的方法
4.因为上述操作会导致数据库中不同图片的路径对应同一个磁盘中的图片,所以再删除时需要注意再次进行判断是否删除磁盘图片(在删除数据库图片信息后与删除磁盘图片前,判断数据库中是否存在该md5值的图片,如果有则不删除,没有则删除)
该代码放在pom.xml中
<dependency>
<groupId>commons-codecgroupId>
<artifactId>commons-codecartifactId>
<version>1.14version>
dependency>
数据库中可能不止一个md5值相同的图片属性信息,所以我们需要返回Image列表,其他和查找全部图片操作相同
public List<Image> selectMd5(String md5)throws SQLException {
List<Image> images = new ArrayList<>();//查找所有的结果返回不止一条数据,所以用列表存放结果集
Connection connection = DBUtil.getConnection();//建立数据库连接
String SQL= "select * from image where imageId = ?";//构造SQL语言
PreparedStatement statement = null;//statement 用于拼接SQL语言
ResultSet resultSet = null;
try{
statement = connection.prepareStatement(SQL);//拼接SQL语言
statement.setString(1,md5);
//执行SQL
resultSet = statement.executeQuery();
//处理结果集
while (resultSet.next()){
Image image = new Image();
image.setImageId(resultSet.getInt("imageID"));
image.setImageName(resultSet.getString("imageName"));
image.setSize(resultSet.getInt("size"));
image.setUploadTime(resultSet.getString("uploadTime"));
image.setContentType(resultSet.getString("contentType"));
image.setPath(resultSet.getString("path"));
image.setMd5(resultSet.getString("Md5"));
image.setSort(resultSet.getString("sort"));
images.add(image);
}
return images;
}catch (SQLException e){
e.printStackTrace();
}finally {
DBUtil.close(connection,statement, resultSet);
}
return null;
}
1.md5计算:
//md5计算
image.setMd5(DigestUtils.md5Hex(fileItem.get()));
2.图片存储地址更改:(由时间戳存储改为md5存储)
image.setPath("./image/" + image.getMd5());
3.判断图片是否写入磁盘:
在将图片属性信息存入数据库之前先查找该图片的md5值相同的图片有多少,返回一个图片列表
然后判断该列表是否为空,为空则写入磁盘
//调用 imageDao.selectMd5()方法
List<Image> imageServletMd5 = imageDao.selectMd5(image.getMd5());
if (imageServletMd5 == null || imageServletMd5.isEmpty()) {
//2.将图片内容存入磁盘
File file = new File(image.getPath());
System.out.println(file);
try {
fileItem.write(file);
System.out.println(fileItem);
} catch (Exception e) {
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{\"ok\":false ,\"reason\":\"写入磁盘失败\"}");
}
}
删除数据库图片信息后与删除磁盘图片前先确认该数据库中是否还有和该图片Md5值相同的图片,若有泽不删除磁盘图片,无则删除
//3.删除数据库中的图片信息
imageDao.deleteOneId(Integer.parseInt(imageId));
//判断数据库中该image的md5是否有数据,如若有责不删除磁盘,如果没有则删除磁盘数据
List<Image> imageServletMd5 = imageDao.selectMd5(image.getMd5());
if (imageServletMd5 == null || imageServletMd5.isEmpty()){
//4.删除磁盘中的图片信息
File file = new File(image.getPath());
file.delete();
}
resp.setStatus(200);
resp.getWriter().write("{\"ok\":true}");
实现防盗链可以使用最简单的白名单来设置,在白名单中设置允许调用这个方法(查看图片)的上一跳页面
可以定义一个HashSet< String >集合类型的whiteList,用来存放允许访问的页面的URL
然后在运行查看图片的代码之前判断上一跳的页面的URL是否在白名单里面,在的话就继续执行查看图片的代码,不在则不执行查看图片的代码,而是返回一个:“非法访问”。
public class ImageShowServlet extends HttpServlet {
static private HashSet<String> whiteList = new HashSet<>();
//创建一个静态私有HashSet集合类型为String,名为whiteList
static {
//在静态块中初始化whiteList这个静态私有HashSet集合
whiteList.add("http://127.0.0.1:8080/img/index.html");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf-8");
String referer = req.getHeader("referer");
//HttpServletRequest对象的一个方法,从HTTP请求中获取名称为:"referer"的头部信息,并将其存储在字符串变量referer中
// "referer"头部信息通常包含了发起请求的页面的URL
if(!whiteList.contains(referer)){
//检查referer是否在白名单集合whiteList中,如果不在,返回false,!false为true,即执行{}中的代码
resp.getWriter().write("{\"ok\":false ,\"reason\":\"不合法的访问\"}");
return;
}
//...具体实现查看图片的代码...
}
}
在现在的图片服务器已经实现的功能里我们还只能一起看到所有的图片这样对于用户十分不方便,用户上传的图片较多时会导致用户查找某一张图片十分不方便,所以我们可以给图片加一个分类,再上传图片的时候选择图片分类,在查看图片时也能做到分类查看
1.数据库:
分类查看图片首先我们需要在数据库中存储下图片类型,所以数据库表的字段中要增加一列:sort
2.设计分类查找的前后端交互API
(1)请求:
./ImageSortServlet?sort="类型"
(2)响应:
HTTP/1.1 200 OK
content-type:image/png
[图片的二进制内容]
{
“OK” : true;
}3.Image
数据库中增加了一列sort,所以Image中必须增加sort这个变量,且必须有get、set、投String方法
4.from表单
后端需要储存图片的内容类型,但图片类型是需要用户在上传图片时选择的,所以我们需要在上传文件的from表单中加入选项,在用户上传图片时可以选择图片的类型
5.ImageDao类中原有方法
因为增加了图片内容类型这一元素,所以在往数据库中写入图片时需要同时写入图片的sort,同时查找图片时也要返回图片的类型sort
6.按照类型sort查找图片属性
ImageDao类中增加查找按照类型sort查找图片属性的方法selectSort(String sort)
这个方法的返回值应该该为List < Image >类型,因为一个类型的图片不止一个7.ImageServlet 类
因为图片新增了一个类型变量,所以在获取from表单传来的数据时需要做一个处理
获取到里面的选项元素信息并且传给image的sort中8.ImageSortServlet类
根据设计的前后端交互API我们需要一个查看不同图片类型的类
在这个类种重写doGet方法,用来查看图片9.WEB.xml绑定URL
我们新增了ImageSortServlet类,要在WEB.xml中将ImageSortServlet类与对应的URL绑定
10.新建.html文件
根据图片内容类型的不同,我们可以创建不同的.html文件,作为不同类型图片展示的页面
11.index.html
更改前端主页面代码
列名 | 含义 | 类型 | 备注 |
---|---|---|---|
imageId | 图片的Id | int | 不可重复,不可为空,自增(现实中用户无法设置图片的Id) |
imageName | 图片名字 | varchaar(50) | |
size | 图片的大小 | int | |
uploadTime | 图片上传时间 | varc har(50) | |
contenType | 图片类型 | varchar(50 | HTTP响应头中的一个字段,用于定义文件的类型和网页编码,决定网页以什么形式读取文件 |
path | 图片路径 | varchar(1024) | 图片会以文件形式存在磁盘,数据库中记录下存储路径对应词攀上的图片 |
md5 | 图片的校验和 | varchar(1024) | 用于提高该系统的安全性(通过一个更短的字符串来验证整体数据是否正确) |
sort | 图片内容类型 | varchaar(50) | 用于保存图片的内容类型,用于分类查看图片 |
在Image类增加下面的代码即可。(toString方法增加子段变为这样即可)
private String sort;//图片类型
public String getSort() {
return sort;
}
public void setSort(String sort) {
this.sort = sort;
}
public String toString() {
return "Image{" +
"imageId=" + imageId +
", imageName='" + imageName + '\'' +
", size=" + size +
", uploadTime='" + uploadTime + '\'' +
", contentType='" + contentType + '\'' +
", path='" + path + '\'' +
", md5='" + md5 + '\'' +
", sort='" + sort + '\'' +
'}';
}
在upload.html文件中更改上传文件的from表单,在from表单中增加一个选项,该选项只能单选,有以下几个选项:植物、动物、风光、人像、美食、其他这六个类别,该选项的名字为:options
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传文件title>
head>
<body>
<form id="upload-form" action="image" method="post" enctype="multipart/form-data" >
<H2>这个文件的类型是:H2>
<select name="options">
<option value="option1">植物option>
<option value="option2">动物option>
<option value="option3">风光option>
<option value="option4">人像option>
<option value="option5">美食option>
<option value="option6">其他option>
select>
<input type="file" id="upload" name="upload" />
<br />
<input type="submit" value="Upload" />
form>
body>
html>
(1)更改insert方法:
更改SQL语句,增加一个占位符?
在给占位符传值是增加一行,传入sort
(2)更改select方法(selectAll等)
在处理结果集的时候记得处理sort
在ImageDao类中新增一个selectSort方法,传入参数为sort返回值为List< Image >,除了SQL语句其他都和selectMd5方法是一样的,SQL语句为按照sort查找数据库
//按照sort查找数据库图片信息
public List<Image> selectSort(String sort)throws SQLException {
List<Image> images = new ArrayList<>();//查找所有的结果返回不止一条数据,所以用列表存放结果集
Connection connection = DBUtil.getConnection();//建立数据库连接
String SQL= "select * from image where sort = ?";//构造SQL语言
PreparedStatement statement = null;//statement 用于拼接SQL语言
ResultSet resultSet = null;
try{
statement = connection.prepareStatement(SQL);//拼接SQL语言
statement.setString(1,sort);
//执行SQL
resultSet = statement.executeQuery();
//处理结果集
while (resultSet.next()){
Image image = new Image();
image.setImageId(resultSet.getInt("imageID"));
image.setImageName(resultSet.getString("imageName"));
image.setSize(resultSet.getInt("size"));
image.setUploadTime(resultSet.getString("uploadTime"));
image.setContentType(resultSet.getString("contentType"));
image.setPath(resultSet.getString("path"));
image.setMd5(resultSet.getString("Md5"));
image.setSort(resultSet.getString("sort"));
images.add(image);
}
return images;
}catch (SQLException e){
e.printStackTrace();
}finally {
DBUtil.close(connection,statement, resultSet);
}
return null;
}
在处理from表单中提交的options选项及文件时,注意后端代码接受参数的方法
from提交表单是采用二进制方式提交的,所以后端代码在处理时就需要判断每个参数的属性,文本就按照文本格式来处理,文件就按照文件格式处理,我们需要用到以下几个包:
pom.xml中加入以下包:
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.4version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.3version>
dependency>
<dependency>
<groupId>commons-collectionsgroupId>
<artifactId>commons-collectionsartifactId>
<version>3.1version>
dependency>
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutilsartifactId>
<version>1.9.2version>
dependency>
在ImageServlet 类的doPost方法中我们需要对获取到的from表单中的参数进行判断,判断是否是文本信息,然后进行分类处理,b便利表单提交过来的内容,使用Iterator迭代器来迭代遍历集合中的元素,将下一个元素存储于fileItem,然后用fileItem.isFormField()方法来判断该元素是不是一个表单字段,如果不是就按照文件处理,如果是就继续判断该元素的名字是不是"options",是就将该元素的值储存与sort中,最终代码如下:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf-8");
//1.获取图片属性数据存入数据库
//(1)创建一个factory对象和fileUpload对象,为获取图片属性做的准备工作(固定逻辑)
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//(2)fileUpload对象进一步解析,将数据存入items
try {
List<FileItem> items = fileUpload.parseRequest(req);
//此时Itmes已经把图片信息获取到了(此时获取到图片信息的可能不止一个图片的,所以用列表存储)
//(3)将fileUpload解析得到的信息存入image
Image image = new Image();
//遍历表单中提交过来的内容
Iterator iterator = items.iterator();
//创建一个迭代器对象,用于遍历集合中的元素
//items: 这是一个集合对象,例如List或者Set,包含了一些元素
//iterator(): 这是集合类的一个方法,用于创建一个迭代器对象。
//Iterator iterator: 这是一个迭代器类型的变量,用于存储创建的迭代器对象。
while (iterator.hasNext()) {
FileItem fileItem = (FileItem) iterator.next();
//iterator: 这是一个迭代器对象,用于遍历集合中的元素
//next(): 这是迭代器的一个方法,用于获取集合中的下一个元素
//FileItem fileItem: 这是一个FileItem类型的变量,用于存储从迭代器中获取的下一个FileItem对象。
if (fileItem.isFormField()) { // 如果是表单域 ,就是非文件上传元素
//isFormField(): 这是 FileItem 类的一个方法,用于判断当前对象是否表示一个表单字段
// 如果当前对象是一个表单字段,那么这个方法将返回 true,否则返回 false。
String value = fileItem.getString("UTF-8");
if(fileItem.getFieldName().equals("options")){
image.setSort(value);
}
}else {
//(3)将fileUpload解析得到的信息存入image
//取出items中存储的众多图片中的第一个图片的信息存储于fileItem
image.setImageName(fileItem.getName());//将取出的数据中文件名赋值给ImageName
image.setSize((int) fileItem.getSize());
//获取当前时间作为存储时间,存储于image中的UploadTime
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
image.setUploadTime(simpleDateFormat.format(new Date()));
image.setContentType(fileItem.getContentType());
//md5计算
image.setMd5(DigestUtils.md5Hex(fileItem.get()));
// 构造一个路径:./image+图片名,但是这个文件存储路径会导致文件名相同时文件路径就会相同,无法存储,所以可以在这个路径中加入时间戳(毫秒级)来分开两个文件
//使用md5作为文件路径
image.setPath("./image/" + image.getMd5());
ImageDao imageDao = new ImageDao();
//先查找存入图片的md5值是否在数据库中有相同的了,没有会返回空值,此时存入磁盘
List<Image> imageServletMd5 = imageDao.selectMd5(image.getMd5());
System.out.println(imageServletMd5);
if (imageServletMd5 == null || imageServletMd5.isEmpty()) {
//2.将图片内容存入磁盘
File file = new File(image.getPath());
//System.out.println(file);
try {
fileItem.write(file);
//System.out.println(fileItem);
} catch (Exception e) {
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{\"ok\":false ,\"reason\":\"写入磁盘失败\"}");
}
}
//(4)将数据存入数据库,利用imageDao中的insert方法
imageDao.insert(image);
}
}
} catch (FileUploadException e) {
//解析失败的情况
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{\"ok\":false ,\"reason\":\"请求解析失败\"}");
} catch (SQLException e) {
throw new RuntimeException(e);
}
//3.给客户端返回一个结果
resp.sendRedirect("index.html");
}
新建ImageSortServlet类重写doGet方法,作为按照类型查看图片时触发的类(API中设置分类查看图片的方法为GET方法)
当前方法需要获取URL中的sort信息,用该信息来查找图片
public class ImageSortServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf-8");
String sort = req.getParameter("sort");
if (sort == null || sort.equals("")){
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{\"ok\":false ,\"reason\":\"sort为空\"}");
}else {
ImageDao imageDao = new ImageDao();
try {
List<Image> images = imageDao.selectSort(sort);
Gson gson = new GsonBuilder().create();
String jsonData = gson.toJson(images) ;
//3.将Json字符串写入返回值resp中
resp.getWriter().write(jsonData);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
绑定ImageSortServlet与出发该类的URL/imageSelectSort
<servlet>
<servlet-name>ImageSortServletservlet-name>
<servlet-class>api.ImageSortServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>ImageSortServletservlet-name>
<url-pattern>/imageSelectSorturl-pattern>
servlet-mapping>
按照前面的分类我们需要六个不同查看类型图片的页面
名称 | 文件名 |
---|---|
植物 | other.html |
动物 | animal.html |
风光 | Scenery.html |
人像 | portrait.html |
美食 | delicacy.html |
其他 | other.html |
other.html:
注意ajax中的URL记得更改:url:“imageSelectSort?sort=option6”
option6是因为在我们进行数据库存储时图片的内容类型就设置成了option1-option6,分别代表对应的不同的类别,具体对应请看upload.html文件
DOCTYPE html>
<html>
<head>
<title>大饼的图片服务器-其他title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Your site description" />
<link rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Lato:700,300" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="css/normalize.min.css" />
<link rel="stylesheet" type="text/css" href="css/style.lite.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js">script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js">script>
head>
<body id="parent">
<div class="main-container">
<div class="header">
<div class="clearfix">
<div class="float-left logo">
<h1>
<a href="#">
大饼的图片服务器-其他
a>
h1>
div>
<div class="float-right">
<ul id="top-nav" class="slimmenu">
<li>
<a href="./upload.html" >上传文件a>
li>
<li>
<a href="#">分类a>
<ul>
<li>
<a href="./index.html" class="filter-label" data-filter=".cat-1">所有a>
li>
<li>
<a href="./plant.html" class="filter-label" data-filter=".cat-1">植物a>
li>
<li>
<a href="./animal.html" class="filter-label" data-filter=".cat-2">动物a>
li>
<li>
<a href="./Scenery.html" class="filter-label" data-filter=".cat-3">风光a>
li>
<li>
<a href="./portrait.html" class="filter-label" data-filter=".cat-1">人像a>
li>
<li>
<a href="./delicacy.html" class="filter-label" data-filter=".cat-2">美食a>
li>
ul>
li>
<li>
<a href="#" class="fullscreen tooltip" title="Go Full Screen">
<span class="pictogram">span>
a>
<a href="#" class="fullscreenExit tooltip" title="Exit Full Screen">
<span class="pictogram">span>
a>
li>
ul>
div>
div>
div>
div>
<div id="freewall" class="free-wall" >
<div id ="image" >
<div v-for="image in images" >
<div class="brick tint size11 cat-2">
<img v-bind:src = "'imageShow?imageId='+image.imageId" alt="" />
<div class="overlay">
<h3 cla ss="project-title">欢迎查看其他类型图片h3>
<p class="project-description">这张图片是{{image.imageName}}p>
<a class="open-project" v-on:click="remove(image.imageId)"><span class="pictogram">♲span> 删除a>
div>
div>
div>
div>
div>
<iframe id="open-iframe" name="open-iframe">iframe>
<script type="text/javascript" src="js/jquery-1.10.2.min.js">script>
<script type="text/javascript" src="js/freewall.min.js">script>
<script type="text/javascript" src="js/jquery.cycle2.min.js">script>
<script type="text/javascript" src="js/jquery.slimmenu.min.js">script>
<script type="text/javascript" src="js/custom-script.min.js">script>
<script>
var app = new Vue({
el:'#image',
data:{
images: [
]
},
methods:{
//GET/image
getImages(){
$.ajax({
url:"imageSelectSort?sort=option6",
type:"get",
context:this,
success:function(data,status){
//发送请求成功的情况下执行,将获取到的返回值传给images
this.images = data;
$('#app').resize();
}
})
},
remove(imageId){
$.ajax({
url:"image?imageId="+imageId,
type:"delete",
context:this,
success:function(data,status){
this.getImages();
alert("删除成功,请刷新");
}
})
},
}
})
app.getImages();
script>
body>
html>
记得在右上角加入查看不同图片的跳转链接,每个类的页面上也要放上这个链接,用来跳转
<li>
<a href="#">分类a>
<ul>
<li>
<a href="./index.html" class="filter-label" data-filter=".cat-1">所有a>
li>
<li>
<a href="./plant.html" class="filter-label" data-filter=".cat-1">植物a>
li>
<li>
<a href="./animal.html" class="filter-label" data-filter=".cat-2">动物a>
li>
<li>
<a href="./Scenery.html" class="filter-label" data-filter=".cat-3">风光a>
li>
<li>
<a href="./portrait.html" class="filter-label" data-filter=".cat-1">人像a>
li>
<li>
<a href="./delicacy.html" class="filter-label" data-filter=".cat-2">美食a>
li>
ul>
li>
记得将新建的几个页面的URL加入查看图片的白名单中
static private HashSet<String> whiteList = new HashSet<>();
//创建一个静态私有HashSet集合类型为String,名为whiteList
static {
//在静态块中初始化whiteList这个静态私有HashSet集合
whiteList.add("http://127.0.0.1:8080/img/index.html");
whiteList.add("http://127.0.0.1:8080/img/plant.html");
whiteList.add("http://127.0.0.1:8080/img/animal.html");
whiteList.add("http://127.0.0.1:8080/img/Scenery.html");
whiteList.add("http://127.0.0.1:8080/img/portrait.html");
whiteList.add("http://127.0.0.1:8080/img/delicacy.html");
whiteList.add("http://127.0.0.1:8080/img/other.html");
}