我的云相册目录
一.引言
二.项目功能
三.技术选型
四.服务器设计
数据库设计
前后端交互接口设计(服务器API设计)
五.基于Servlet搭建服务器(后端)
新增图片
查看图片信息
删除图片
查看图片内容
六.前端页面设计
七.细节优化
八.效果展示
云相册其实就是一个可以存储图片的HTTP服务器,可以完成对图片的增删改查功能,同时搭配简单的前端页面辅助完成图片上传和展示。
实现图片的上传,查询,删除,以及展示功能
1.数据库存储图片元信息——MySQl
2.使用JDBC操作数据库
3.前后端交互——Ajax异步提交
4.文件上传------使用了第三方库commons-fileupload
5.磁盘的存储优化——Md5
6使用Gson这个库完成json的解析与构造
7.使用Postman工具进行简单的测试
8.使用html,css,JavaScript技术构建简单的网页
9.使用tomcat部署项目
1.创建数据库表,存储图片的属性信息
create table image_table(imageId int not null primary key auto_increment,
imageName varchar(50),
size int,
uploadTime varchar(50),
contentType varchar(50),
path varchar(1024),
md5 varchar(1024)
);
2.封装数据库操作
1)创建DBUtil类,辅助创建连接
public class DBUtil {
private static final String URL="jdbc:mysql://127.0.0.1:3306/java_image_server?characterEncoding=utf8&&useSSL=true";
private static final String USERNAME="root";
private static final String PASSWORD="123456";
private static volatile DataSource dataSource=null;
public static DataSource getDataSource(){
}
public static Connection getConnection() {
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
}
}
2)创建Image类
public class Image {
private int imageId;
private String imageName;
private int size;
private String uploadTime;
private String contentType;
private String path;
private String md5;
}
3)创建ImageDao类,实现对Image对象的增删改查
public class ImageDao {
/*
* 功能描述:把image对象插入到数据库中
* @return void
*/
public void insert(Image image){
}
/*
* 功能描述:查找数据库中所有图片的信息
* @return java.util.List
*/
public List selectAll(){
}
/*
* 功能描述:根据imageId查找指定的图片信息
* @return dao.Image
*/
public Image selectOne(int imageId){
}
/*
* 功能描述:根据imageId删除指定的图片
* @return void
*/
public void delete(int imageId){
}
//按照md5查找
public Image selectByMd5(String md5){
}
}
前置知识:在服务器api设计部分,我们将会用到一种轻量级的数据交换格式——JSON。json只是一种数据格式,和编程语言无关(出身于JavaScript), json可以将对象中表示的一组数据转换为字符串,然后可以在网络或者程序之间轻松的传递这个字符串,并在需要的时候将它还原为各编程语言所支持的数据格式。我们在这里使用json完成数据的序列化,方便进行网络传输。
Gson:是google搞的开源的JSON解析库,我们在使用json时需要将gson库加入我们的依赖中,可在maven仓库中进行下载:
com.google.code.gson
gson
2.8.6
下面以一个简单示例来学习Json的使用:
public class TestGson {
public static void main(String[] args) {
HashMaphashmap=new HashMap<>();
hashmap.put("name","曹操");
hashmap.put("skill1","剑气");
hashmap.put("skill2","三段跳");
hashmap.put("skill3","加攻击并吸血");
hashmap.put("skill4","加攻速");
//通过map转成一个JSON结构的字符串
//1.创建一个gson对象
Gson gson=new GsonBuilder().create();
//2.使用toGson方法把键值对结构转换成JSON字符串
String str=gson.toJson(hashmap);
System.out.println(str);
}
}
打印结果为一组键值对的集合:
接下来开始正式设计前后端交互api,即我们的HTTP协议要构建成什么样子。在此部分我们使用body字段中ok为true表示成功,ok为false表示失败,或者[ ]有内容表示成功,[ ]没有内容表示失败。
1.新增图片
首先写一个简单的的upload.html来上传图片:
设定接口如下:
2.查看所有图片属性
3.查看指定图片属性
}
4.删除指定图片
5.查看指定图片内容
此部分需要创建两个Servlet类,一个用来完成图片的增删改查(ImageServlet),另一个用来展示图片的详细内容(ImageShowServlet),这两个类都需要继承HttpServlet。
HttpServlet中提供的doXXX系列方法和HTTP协议是一一对应的, 例如:
如果给服务器发送的是get请求,tomcat就会自动调用HttpServlet中的doGet()方法;
如果给服务器发送的是post请求,tomcat就会自动调用HttpServlet中的doPost()方法;
如果给服务器发送的是get请求,tomcat就会自动调用HttpServlet中的doDelete()方法;
新增图片对应ImageServlet类的doPost方法,这里需要用到Commons FileUpload,需要在maven仓库中下载jar包。
commons-fileupload
commons-fileupload
1.4
具体操作:
1.获取到图片相关的属性信息(Image对象),并写入数据库。
2.获取到图片内容,写入到磁盘中
3.设置返回的响应结果
代码片段:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取图片的属性信息,并且存入数据库
//1.1 需要创建一个factory对象,和upload对象,这是为了获取图片属性所做的准备工作
// 固定的逻辑
FileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload upload=new ServletFileUpload(factory);
//1.2 通过upload对象进一步解析请求(解析HTTP请求中奇怪的body中的内容)
// FileItem就代表一个上传的文件对象
// 理论上来说,HTTP支持一个请求中同时上传多个文件
Listitems=null;
try {
items=upload.parseRequest(req);
} catch (FileUploadException e) {
//出现异常说明解析错误!
e.printStackTrace();
resp.setContentType("application/json; charset=utf-8");
//告诉客户端出现的具体的错误是啥
resp.getWriter().write("{\"ok\":false,\"reason\":\"请求解析失败\"}");
return;
}
//1.3 把FileItem中的属性提取出来。转换成Image对象,才能存到数据库中
// 当前只考虑一张图片的情况
FileItem fileItem=items.get(0);
Image image=new Image();
image.setImageName(fileItem.getName());
image.setSize((int)fileItem.getSize());
//手动获取当前日期,并转成格式化日期
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMdd");
image.setUploadTime(simpleDateFormat.format(new Date()));
image.setContentType(fileItem.getContentType());
//自己构造一个路径来保存,引入一个时间戳是为了能够让文件路径能够唯一
//image.setPath("./image/"+System.currentTimeMillis()+"_"+image.getImageName());
//image.setMd5("11223344");
//存到数据库中
ImageDao imageDao=new ImageDao();
imageDao.insert(image);
//2.获取图片内容信息,并且写入磁盘文件
File file=new File(image.getPath());
if (existImage==null) {
try {
fileItem.write(file);
} catch (Exception e) {
e.printStackTrace();
resp.setContentType("application/json; charset=utf-8");
resp.getWriter().write("{\"ok\":false,\"reason\":\"写磁盘失败\"}");
return;
}
}
//3.跟客户端返回一个结果数据
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{\"ok\":true}");
}
测试:使用upload.html上传一个图片,检验服务器响应是否为{ok:true},并且查看数据库是否写入成功。
选择一张图片上传后,服务器的响应如下,代表上传成功。
这里分成两种情况,一是查看所有图片信息,二是查看指定图片信息。根据请求中是否含有imageId这个参数来决定。含有imageId参数的请求是查看指定图片信息,未含的是默认查看所有图片信息。
获取图片信息对应于ImageServlet类中的doGet()方法:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//考虑到查看所有图片属性和查看指定图片属性
//通过是否URL中带有参数来进行区分
//存在imageId则查看指定图片属性,否则就查看所有图片属性
//例如:URL/image?imageId=100,imageId的值就是“100”
//如果imageId中不存在imageId,那么返回null
String imageId=req.getParameter("imageId");
if(imageId==null||imageId.equals("")){
//查看所有图片属性
selectAll(imageId,resp);
}else{
//查看指定图片
selectOne(imageId,resp);
}
}
1.查看所有图片属性信息
private void selectAll(String imageId, HttpServletResponse resp) throws IOException {
resp.setContentType("application/json;charset=utf-8");
//1.创建一个ImageDao对象,并查找数据库
ImageDao imageDao=new ImageDao();
Listimages=imageDao.selectAll();
//2.把查找到的结果转成JSON格式的字符串,并且写回给resp对象
Gson gson=new GsonBuilder().create();
//jsonData就是一个json格式的字符串了,就和之前约定的格式是一样的了
//重点体会下面这行代码,这个方法的核心,gson帮忙自动完成了大量的格式转换工作
//只要把之前的相关的字段都约定成统一的命名,下面的操作就可以一步到位的完成整个转换
String jsonData=gson.toJson(images);
resp.getWriter().write(jsonData);
}
2.查看指定图片属性信息
private void selectOne(String imageId, HttpServletResponse resp) throws IOException {
resp.setContentType("application/json;charset=utf-8");
//1.创建ImageDao对象
ImageDao imageDao=new ImageDao();
Image image=imageDao.selectOne(Integer.parseInt(imageId));
//2.使用gson把查到的数据转换成json格式,并写回给响应对象
Gson gson=new GsonBuilder().create();
String jsonData=gson.toJson(image);
resp.getWriter().write(jsonData);
}
测试查询所有图片信息:
测试查询指定图片信息:image?imageId=12——查询imageId=12的图片元信息
删除图片对应ImageServlet类中的doDelet()方法。
具体步骤:1.解析请求中的imageId
2.查找数据库,根据imageId删除数据库中的信息
3.根据路径从文件中读取图片数据并且删除,还要记得设置content-type。
代码片段:
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf-8");
//1.先获取到请求中的imageId
String imageId=req.getParameter("imageId");
if(imageId==null||imageId.equals("")){
resp.setStatus(200);
resp.getWriter().write("{\"ok\":false,\"reason\":\"解析请求失败\"}");
return;
}
//2.创建ImageDao对象,查看该图片对象对应的相关属性(这是为了知道这个图片对应的文件路径)
ImageDao imageDao=new ImageDao();
Image image=imageDao.selectOne(Integer.parseInt(imageId));
if(image==null){
//此时请求中传入的id在数据库中不存在
resp.setStatus(200);
resp.getWriter().write("{\"ok\":false,\"reason\":\"imageId在数据库中不存在\"}");
return;
}
//3.删除数据库记录
imageDao.delete(Integer.parseInt(imageId));
//4.删除本地磁盘文件
File file=new File(image.getPath());
file.delete();
resp.setStatus(200);
resp.getWriter().write("{\"ok\":true}");
return;
}
测试删除功能:由于删除功能的实现需要用到js绑定事件,而目前还没有实现到前端部分,因此在这里我用到了一个HTTP服务器的测试工具——Postman,它可以很方便的构造各种请求。
首先打开Postman新建一个请求,输入请求的url:http://localhost:9999/imageServer/image?imageId=19,方法选择get,可以查询到imageId=19的图片的属性信息,如下图:
然后再将方法改为Delete,我们可以看到body部分显示了一个json格式的字符串,{"ok":true},此响应表明删除成功了,再通过查询数据库发现imageId=19的图片信息已经不存在了,则说明我们的代码是没有问题的,测试成功。
查看图片内容对应ImageShowServlet类的doGet()方法
具体步骤:1.解析请求中的ImageId
2.根据imageId查找数据库,找到对应的path
3.根据路径从文件中读取图片数据,并且设置content-type
代码片段:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.解析出imageId
String imageId=req.getParameter("imageId");
if(imageId==null||imageId.equals("")){
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{\"ok\":false,\"reason\":\"imageId解析失败\"}");
return;
}
//2.根据imageId查找数据库,得到相应的图片属性信息(需要知道图片存储的路径)
ImageDao imageDao=new ImageDao();
Image image=imageDao.selectOne(Integer.parseInt(imageId));
//3.根据路径打开文件,读取其中的内容,写入到响应对象中
OutputStream outputStream=resp.getOutputStream();
resp.setContentType(image.getContentType());
File file=new File(image.getPath());
FileInputStream fileInputStream=new FileInputStream(file);//通过字节流对象读取数据
byte[] buffer=new byte[1024];//缓冲区,使得读取效率更高
while(true){
int len=fileInputStream.read(buffer);//把数据读到buffer里
if(len==-1){
//文件读取结束
break;
}
//此时已经读到一部分数据,放到buffer里,把buffer中的内容写到响应对象中
outputStream.write(buffer);
}
fileInputStream.close();
outputStream.close();
}
测试查看图片内容:例如查找imageId=12的图片内容
对于前端部分,现在网上有很多现成的漂亮的网页模板(模板中心),我们可以直接基于现成的页面进行修改。我在网上找了一个HTML模板zmaze ui,将网页模板解压缩,然后拷贝到webapp目录中。
1.导航栏实现上传图片功能代码:
修改过程
为了让"上传按钮"和前面一样高,在这里加了style="height:41px"。
2.实现展示图片
页面主题展示图片的预览代码: