个人项目-图片服务器

article/2025/10/2 23:00:28

一、项目背景

图床, 解决 github / 博客中插入图片的问题。

二、整体架构

核心就是一个 HTTP 服务器, 提供对图片的增删改查能力.
同时搭配简单的页面辅助完成图片上传/展示。需要实现的功能/接口:

  • 显示图片列表;
  • 显示图片内容;
  • 上传图片;
  • 删除图片;

三、技术分析

  • 因为存在删除、上传的操作,所以前端页面需要是动态网页。实现动态网页的技术有三种:(1)servlet返回字符串拼接的html内容;(2)模板技术(3)ajax动态响应动态生成html内容。本次项目使用第三种方式。
  • 图片的信息,包括:名称,Http路径,图片重复标识(为了避免上传的图片重复而存在的唯一标识,类似于hashcode字段)。信息需要存储在数据库中,返回在图片列表里,数据类型是List< T >。
  • 图片的数据:在页面初始化的时候就由浏览器自动发ajax请求,展示出来。

四、业务设计

  • 数据库设计:考虑包括的字段
  • 接口设计:考虑请求方法,请求数据格式;响应数据
  • 开发环境:前端(ajax,vue,jquery);后端:servlet,JDBC,commons-codec(apache提供做加密的依赖包),jackson,lombok。

五、详细设计

1.数据库设计

-- 准备表
create table image_info(image_id int primary key auto_increment comment'主键id',image_name varchar(50) comment'图片名称',size bigint comment'图片大小',upload_time datetime comment'图片上传日期',md5 varchar(128) comment 'md5值,用于校验图片唯一',content_type varchar(50) comment '数据类型,上传图片时包含在报文中的 ',path varchar(1024) comment'图片路径:相对路径');

2.实体类设计

在数据库建立好后,需要根据字段实现实体类,因为JDBC操作及返回http响应都需要使用实体类;

//lombok注解,加上以后,自动在编译的class文件中生成这些方法
@Getter
@Setter
@ToString
public class ImageInfo {private Integer imageId;private String imageName ;private long size;private java.util.Date uplaodeTime;private String md5;private String contentType;private String path;
}

3.前端设计

UI框架需要自己在网上找来使用,这里只做涉及到前后端交互的接口。

  • 采用了VUE框架(可以让数据和DOM元素双向绑定),同时使用了jQuery中封装好的ajax函数来渲染页面和发送请求数据。VUE版本:2.6.12
  • 前端的执行流程:
  1. 访问index.html,页面初始化就发送Ajax请求获取图片列表信息。 对于返回的响应(这里要求返回imageID和imageName两个字段)通过vue的绑定渲染到相应的位置。从而显示图片列表。

  2. 有了图片列表,浏览器自动根据<img src=" "来发送请求获取图片内容。

  3. 上传图片功能的实现:利用vue绑定提交事件函数,提交form表单。事件函数:
    在这里插入图片描述

  4. 删除图片:利用vue绑定删除事件函数即可。事件函数:
    在这里插入图片描述

<!-- content srart --><div class="am-g am-g-fixed blog-fixed blog-content"><figure data-am-widget="figure" class="am am-figure am-figure-default "   data-am-figure="{  pureview: 'true' }"><div id="container"><div v-for="image in images"><--这里使用了v-bind:src来拼接images数组里的内容--<img v-bind:src="'imageShow?imageId=' + image.imageId" style="height:200px; width:200px"><h3 style="margin-left: auto; margin-right: auto;">{{image.imageName}}</h3><button style="width:100%" class="am-btn am-btn-success" v-on:click.stop="remove(image.imageId)">删除</button></div></div></figure></div>
</div>
<script>var app = new Vue({el:'#app',data: {images: [],uploadImage: ''},methods: {getImages() {$.ajax({url: "image",type: "get",context: this,//响应码200时执行,将服务端响应的data赋值给images对象,随后便通过vue的绑定修改相应的dom,以后便可以渲染到页面上,success: function(data, status) {this.images = data;$("#app").resize();}})},changeImage(event){app.uploadImage = event.target.files[0];},imageUpload(){if(!app.uploadImage) {alert("选择图片后上传");return;}let data = new FormData();data.append("uploadImage", app.uploadImage);$.ajax({url: "image",type: "post",processData: false,contentType: false,data: data,// context: this,success: function(data, status) {if(data.ok){app.getImages();}else{alert(data.msg);}// alert("上传成功");},error: function (err, textStatus, throwable) {console.error(JSON.stringify(err))}})},remove(imageId) {$.ajax({url:"image?imageId=" + imageId,type:"delete",context: this,success: function(data, status) {app.getImages();alert("删除成功");}})}},});app.getImages();
</script>

4.API设计

(1)常用功能封装

  • 封装数据库连接池
public class DBUtil {//双重校验锁方式,封装数据库连接池(单例模式)//1.volatile修饰静态变量//2.两个if判断,中间进行synchronized加锁操作private static volatile DataSource DS;private static DataSource getDtaSource(){if(DS==null){synchronized (DBUtil.class){if(DS==null){MysqlDataSource dataSource=new MysqlDataSource();dataSource.setURL("jdbc:mysql://localhost:3306/image_system");dataSource.setUser("root");dataSource.setPassword("666666");dataSource.setUseSSL(false);dataSource.setCharacterEncoding("utf8");DS=dataSource;}}}return DS;}//提供一个开放的连接方式public static Connection getConnection(){try {return getDtaSource().getConnection();} catch (SQLException e) {throw new RuntimeException("获取数据库连接失败",e);}}@Testpublic void testGetConnection(){System.out.println(getConnection());}public static void close(Connection c, Statement s, ResultSet rs){try {if(rs!=null) rs.close();if(s!=null) s.close();if(c!=null) c.close();} catch (SQLException e) {throw new RuntimeException("释放数据库资源出错",e);}}
}
  • 序列化与反序列化操作封装
public class WebUtil {private static final ObjectMapper M=new ObjectMapper();static {//设置日期格式DateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");M.setDateFormat(df);}//json序列化public static void serialize(HttpServletResponse resp,Object o) throws IOException {resp.setContentType("application/json");resp.setCharacterEncoding("UTF-8");try {String json=M.writeValueAsString(o);resp.getWriter().write(json);} catch (IOException e) {e.printStackTrace();resp.setStatus(500);
//            Map<String,Object> body=new HashMap<>();
//            body.put("ok",false);
//            body.put("msg","json序列化失败");
//            resp.getWriter().write(M.writeValueAsString(body));}}//反序列化public static <T> T deserialize(HttpServletRequest req,Class<T> clazz){try {return M.readValue(req.getInputStream(),clazz);} catch (IOException e) {throw new RuntimeException("反序列化失败",e);}}
}

(2)针对路径问题的考虑

  • 上传需要考虑到的图片路径的问题:上传到数据库时,需要保存Path;图片本身是要保存在本地硬盘上,也涉及到路径;显示文件内容时,前端也需要路径< img src=“xxxx”;因此需要综合考虑这些路径之间的关系。
  • 对于上传到数据库中保存的路径信息字段名为path(是服务端自定义的,这里是一个md5值),但是数据库中并不保存完整路径,完整路径=本地路径前缀+自定义后缀
  • 前端显示的路径由<img v-bind:src="'imageShow?imageId=' + image.imageId">决定
  • 后端servlet需要提供imageShow的接口:通过解析imageID,找到文件在本地的路径(完整路径),然后把二进制数据写入响应体。其中解析方法是:1,通过Id在数据库中找到对应的数据(包含path字段)2.拼接上前缀就可以找到图片在本地的真实路径;

(3)上传接口设计

上传的流程:提出上传请求(前端)-》接收请求(后端)-》上传数据(后端)-》图片保存在服务端硬盘(后端)-》图片信息添加到数据库中(后端)-》返回响应(后端)-》接收响应(前端)-》渲染(前端)
接口实现的步骤:

  • 获取请求数据:获取图片Part对象
  • 保存上传图片到本地
  • 保存图片信息在数据库
  • 构建响应数据

上传数据使用到的工具:@MultipartConfig注解

@WebServlet("/image")
@MultipartConfig
public class imageServlet extends HttpServlet {public static final String LOCAL_PATH_PREFIX="D:\\Develop\\Java_Develop\\图片服务器\\TMP";//图片上传接口@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("utf-8");//请求数据:uploadImage=图片数据Part p=req.getPart("uploadImage");//TODO 先验证MD5值//1.保存在服务端本地:文件名为md5名String md5= DigestUtils.md5Hex(p.getInputStream());//根据md5值查找数据ImageInfo imageInfo=imageDao.selectByMd5(md5);if(imageInfo!=null){//已经存在图片信息,说明上传重复Map<String,Object> data=new HashMap<>();data.put("ok",false);data.put("msg","上传图片重复");WebUtil.serialize(resp,data);return;}p.write(LOCAL_PATH_PREFIX+"/"+md5);//2.保存在数据库//先构造数据库保存的信息ImageInfo image=new ImageInfo();image.setImageName(p.getSubmittedFileName());image.setSize(p.getSize());image.setUplaodeTime(new java.util.Date());image.setMd5(md5);image.setContentType(p.getContentType());image.setPath("/"+md5);//插入数据库数据int n= imageDao.insert(image);Map<String,Object> data=new HashMap<>();data.put("ok",true);WebUtil.serialize(resp,data);}

(4)获取图片列表接口设计

这一部分只是获取到图片的信息,图片本身还没有获取到。针对这个接口,servlet需要做的是:查询所有图片的数据,多行数据转换为List< ImageInfo >,并设置到响应体返回。

//获取图片列表接口;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//查询数据库所有图片并返回List<ImageInfo> images=imageDao.selectAll();//返回响应WebUtil.serialize(resp,images);}

(5)获取图片内容接口

在前端:<img v-bind:src="'imageShow?imageId=' + image.imageId">会在页面初始化时自动发出请求获取图片内容。请求:“GET” imageShow?imageId=n;
而servlet响应体设置为图片的二进制数据:使用了文件工具类Files的readAllByte(Path)方法,以二进制返回一个路径下的所有数据, byte[] data=Files.readAllBytes(pic.toPath());.其中Path对象可以根据File对象来转换。

//获取图片内容接口@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取请求数据:获取图片IdString imageId =req.getParameter("imageId");//2.根据图片Id,在数据库查询图片数据ImageInfo imageInfo= imageDao.selectOne(Integer.parseInt(imageId));//3.返回响应String path=imageServlet.LOCAL_PATH_PREFIX+imageInfo.getPath();//读取这个路径的文件File pic=new File(path);byte[] data=Files.readAllBytes(pic.toPath());//把二进制图片数据写入到响应正文resp.getOutputStream().write(data);}
}

(6)删除图片接口

这一部分在前端的设计是:
在这里插入图片描述
因此在后端设计中是根据图片ID来删除。在从本地删除pic.delete();的同时,也要从数据库中删除,这里封装了函数imageDao.delete(id);
存在问题:本地文件若存储在C盘,ideal是没有权限删除文件的,pic.delete();会返回false;本项目没有考虑到删除失败的问题。


//删除接口@Overrideprotected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.获取图片idString imageId=req.getParameter("imageId");Integer id=Integer.parseInt(imageId);//2.删除本地文件,先查询ImageInfo imageInfo=imageDao.selectOne(id);String path=LOCAL_PATH_PREFIX+imageInfo.getPath();File pic=new File(path);//删除文件pic.delete();//3.删除数据库中数据int n=imageDao.delete(id);//4.返回响应数据dataMap<String ,Object>data=new HashMap<>();data.put("ok",true);WebUtil.serialize(resp,data);}

5.项目优化

目前存在着一些问题:
1.重复图片上传的验证
对于数据库来说即使是重复的图片仍能上传到数据库,产生了新的数据,所以会降低性能;对于本地文件来说,是会覆盖掉,所以本地问题不大。

解决方案:校验Md5值,即在保存之前先根据md5值在数据库中查找一遍,如果有查找到数据,就不再保存。

ImageInfo imageInfo=imageDao.selectByMd5(md5);if(imageInfo!=null){//已经存在图片信息,说明上传重复Map<String,Object> data=new HashMap<>();data.put("ok",false);data.put("msg","上传图片重复");WebUtil.serialize(resp,data);return;}

2.图片防盗链

很多网站会在html中的src中引用别人的图片,如果仅想被白名单网站引用,就要设置防盗链。
解决方案:根据请求报文中Referer字段的值来判断这是哪个网页发起的请求,同时决定要不要让他访问。
在图片显示servlet里加上白名单列表:

 //白名单列表:如果放云服务器是需要改地址private static final List<String> WHITE_LIST= Arrays.asList("http://localhost:8080/java_image_server/","http://localhost:8080/java_image_server/index.html");

同时在doGet方法中加上:


//防盗链String referer=req.getHeader("Referer");//不在白名单中if(!WHITE_LIST.contains(referer)){resp.setStatus(403);return;}

http://chatgpt.dhexx.cn/article/VkVBdj3N.shtml

相关文章

Linux搭建图片服务器

目录 1 安装目的 2 下载地址 3 FastDFS安装环境 3.1 安装libevent 3.2 安装libfastcommon 3.3 tracker编译安装 4 配置 5 启动 5.1 设置开机自动启动。 5.2 FastDFS--storage安装 5.3 安装libevent 5.4 安装libfastcommon 5.5 storage编译安装 5.6 配置 5.7 启动…

上传图片到服务器

上传图片到服务器&#xff0c;调用海宁写的接口 重点&#xff1a; 1、创建formData对象&#xff0c;写入文件值 2、设置ajax上传时的参数 var formData new FormData();//创建一个新的FormData对象,Ajax中的 data 属性就是 formdataformData.append("files", res…

Nginx做图片服务器

前言&#xff1a; Nginx是一个高性能的HTTP和反向代理web服务器,以及负载均衡器。根据nginx是高性能的http服务器&#xff0c;因此可以用作图片服务器使用。 本案例是在docker安装nginx来操作的。 Nginx安装 1、首先是docker的安装 详情参考&#xff1a;docker安装 2、拉取ng…

搭建图片服务器方案

一、图片服务器机制 为了提高图片服务器性能&#xff0c;可利用Nginx中反向代理缓存机制达到目的&#xff1b;本机制共需两台服务器&#xff0c;在图片存储服务器外另部署一台服务器作为图片代理服务器&#xff08;在一台服务器上同时部署也可&#xff09;&#xff0c;当客户端…

Nginx配置图片服务器

Nginx配置图片服务器 背景请求示例Nginx配置图片服务器问题存在 背景 在我们日常做项目的时候&#xff0c;我们有时候会经常碰到图片需要放到哪&#xff1f;有时候会用第三方的图片存储&#xff0c;比如腾讯云的COS&#xff0c;阿里云的OOS。当然有时候我们也是需要放到Linux的…

项目:图片存储系统(图片服务器)

图片存储系统 项目描述&#xff1a; 实现一个 HTTP 服务器&#xff0c;用该服务器来存储图片&#xff0c;针对每个图片提供一个唯一的url&#xff0c; 使用 url 对图片进行访问&#xff0c; 提供对图片的增删改查能力&#xff0c;同时搭配简单的页面辅助完成图片上传/展示 利…

搭建Nginx图片服务器

一、安装Nginx 先安装Nginx&#xff0c;看我之前发的文章&#xff1a; 搭建Nginx服务器 二、安装vsftpd 再安装vsftpd组件&#xff0c;看我之前发的文章&#xff1a; Linux安装ftp组件(8步完成) 三、开始搭建Nginx图片服务器 1、效果 例如&#xff1a;图片通过ftp服务上传…

[项目]图片服务器

目录 1. 项目背景2.项目模块划分2.1数据存储模块2.2服务器API 3. 总结4. 扩展 1. 项目背景 现在很多网页都可以见到图片上传功能&#xff0c;我们上传一张本地图片后&#xff0c;网页就会显示我们所上传的图片&#xff0c;比如博客、个人信息提交页面等等。那么这背后的原理是什…

nginx图片服务器

编辑nginx.conf linux下配置文件服务器 配置完以后需要执行nginx -s reload重新加载配置 springboot项目application.properties配置访问和保存图片路径 #nginx文件服务器ip ip127.0.0.1#保存图片绝对路径 save.pic/home/pic/images/#展示图片url get.pichttp://${ip}/image…

如何快速搭建图片服务器

前言 分布式集群的项目, 正常一般的工程是把图片放在web项目的自身服务器的工程中&#xff0c;但在集群环境下&#xff0c;会出现找不到图片的情况。 代码参考: https://github.com/zyjcxc/taotao.git 比如&#xff1a; 解决办法&#xff1a; linux做磁盘的映射&#xf…

图片服务器实现

项目背景 现在很多地方仅仅支持文字发送&#xff0c;而不支持图片发送&#xff0c;&#xff0c;但是在很多特定的场景有需要图片发送等方式&#xff0c;所以我们可以构建一个HTTP服务器来完成这个功能&#xff0c;通过这个服务器为连接服务器的数据库上的每个图片生成一个特定…

图片服务器解决方案

最近经常有人问图片上传怎么做&#xff0c;有哪些方案做比较好&#xff0c;也看到过有关于上传图片的做法&#xff0c;但是都不是最好的 今天再这里简单讲一下上传图片以及图片服务器的大致理念 如果是个人项目或者企业小项目&#xff0c;仅仅只有十来号人使用的小项目&#…

搭建一个图片服务器

最近在学习一个电商项目&#xff0c;其中用到了图片上传服务&#xff0c;自己在学习过程中遇到了点问题&#xff0c;记录下来&#xff0c;以备以后查询 首先需要安装nginx和vsftpd&#xff0c;这两者的安装都有相应的手册&#xff0c;步骤非常详细&#xff0c;我就不啰嗦了&…

简单的本地图片服务器的搭建

简单的本地图片服务器的搭建 第一步&#xff1a;安装部署 Nginx下载 Nginx下载完解压后 第二步&#xff1a; 搭建图片服务器 第一步&#xff1a;安装部署 Nginx 下载 Nginx 保存文件路径不要包含中文&#xff01; Linux和Windows不一样&#xff01; 下载完成后&#xff0c;解…

图片服务器

图片服务器 图片服务器主要的功能是&#xff1a;上传图片&#xff0c;显示图片的功能 写博客的时候&#xff0c;插入的图片&#xff0c;本质上是往文章内插入一个url&#xff0c;图片其实是保存在在另一个服务器上&#xff0c;而我这个项目就是制作一个类似这样的服务器。 核心…

FastDFS搭建图片服务器

服务器规划 服务器名称IP地址和端口备注fastdfs-tracker*:22122跟踪服务器/调度服务器fastdfs-storage*:23000存储服务器 一、安装系统组件 yum install gcc -y 二、安装fastdfs 1、创建图片服务器存储目录 mkdir -p /data/image 2、下载FastDFS依赖包libfastcommon并安…

Zimg—轻量级图片服务器搭建利器

在一个互联网应用中&#xff0c;图片扮演着越来越重要的角色。有稳定的可扩展的图片存储服务器就显得尤为的重要&#xff0c;云厂商们提供了便利的图片存储服务&#xff0c;花钱就可以解决了。这里简单介绍一个开源的一个分布式图片存储服务器——zimg&#xff0c;来自己搭建一…

图片服务器的搭建

当高并发的时候容易发生图盘上传到一个服务器从另一个服务器需要读取照片 解决方法&#xff1a; 专门保存图片&#xff0c;不管是哪个服务器接收到图片&#xff0c;都把图片上传到图片服务器。 图片服务器上需要安装一个http服务器&#xff0c;可以使用tomcat、apache、ngin…

图片服务器搭建

图片服务器搭建 原先我们通过servlet上传一个用户的头像&#xff0c;需要把头像显示到网站上。就需要搭建一个图片服务器来显 示图片了。 图片服务器其实和tomcat/nginx容器的作用是一样的。目的都是要把文件从本地的磁盘上发布出去。一般常用 的图片服务器是apache服务器 &am…

三种图片服务器

2019独角兽企业重金招聘Python工程师标准>>> 到目前&#xff0c;工作中用到的图片服务器有下面三种&#xff1a; &#xff08;1&#xff09;使用Nginx搭建图片服务器 &#xff08;2&#xff09;使用阿里云图片服务器&#xff08;OOS&#xff09; &#xff08;3&…