Java项目---图片服务器

news2025/1/21 16:38:18

图片服务器--->服务器(图床)

  • 核心功能:上传图片、展示图片等

比如:编写博客时我们会插入图片,本质上是往文章中放了一个链接(URL),这个URL资源在另外一个服务器上。

  • 核心知识点:

(1)简单的Web服务器设计开发能力(Servlet)

Web服务器--->HTTP服务器

Servlet是Tomcat这个HTTP服务器所提供的一组编程接口

(2)使用数据库来进行存储(MySQL)JDBC操作MySql

(3)数据库设计(根据实际场景设计数据库表结构)

(4)前后端交互的API的设计(基于HTTP协议)

(5)认识JSON数据格式,学习使用Java中的Gson这个库操作JSON数据

(6)学习测试一个HTTP服务器(Postman)

(7)使用HTML CSS JavaScript技术构建一个简单的网页

一、服务器设计

1.数据库设计

数据库中存储的是图片的属性(元信息);图片正文是以文件的形式直接存在磁盘上的;数据库中就记录一个path,path就对应到磁盘上的一个文件。

  • md5:图片的md5校验和(字符串哈希算法,哈希表)
  • 校验和:通过一个更短的字符串,来验证整体数据是否正确,短的字符串是根据原串内容通过一定的规则来计算出来的。

2.服务器API设计(前后端交互接口设计)

(1)JSON

一种数据组织的格式,格式键值对的结构。JSON只是一种数据格式,和编程语言无关。

JSON结构展示:

{
   "name":"曹操",
   "skill1":"剑气",
   "skill2":"三段跳",
   "skill3":"加攻击和吸血",
   "skill4":"加攻速",
}

使用JSON完成数据的序列化方便完成网络传输。

(2)Gson:google搞得一个开源的JSON解析库

(3)文件上传操作在HTML中是如何完成的(用到form表单)

文件上传在HTTP协议中是如何完成的

  • 开始设计前后端交互API

1>新增图片

请求:使用POST,POST到/image这个路径上

Content-Type:multipart/from-data;

(正文内容包含图片自身的一些信息)

(图片正文的二进制内容)

响应:(1)上传成功

①HTTP/1.1 200 OK 

②{

        "ok":true,

}

(2)上传失败

①HTTP/1.1 200 OK 

②{

        "ok":false,

        "reason":"具体的失败原因"

}

2>查看所有图片信息(数据库里面存的属性信息)

请求:GET/image

响应:①成功:HTTP/1.1 200 OK

成功返回的数组:

[

    {

          imageid:1,

          imageName:"1.png",

          contentType:"image/png",

          size:1000,

          uploadTime:"20200222",

          path:"./data/1.png",

          md5:"11223344",

     },

    {

         .........

    }

]

②:失败:HTTP/1.1 200 OK

失败返回的数组:

[](返回一个空数组)

3>查看指定图片属性

请求:GET/image?imageid=[具体的数值]

响应:

①:成功

HTTP/1.1 200 OK

{

          imageid:1,

          imageName:"1.png",

          contentType:"image/png",

          size:1000,

          uploadTime:"20200222",

          path:"./data/1.png",

          md5:"11223344",

}

②:失败

HTTP/1.1 200 OK

{

       ok:false,

       reason:"具体出错的原因"

}

4>删除指定图片

请求:DELETE/image?image=[具体的图片id]

响应:

①:成功

HTTP/1.1 200 OK

{

       ok:true,

}

②:失败

HTTP/1.1 200 OK

{

        ok:false,

        reason:"具体的出错原因",

}

服务器实现代码的时候就可以判断方法,如果是DELETE方法就执行删除操作

删除不一定非要用DELETE方法,也可以用:GET/image?image=xxx&delete=1

5>查看指定图片内容

请求:GET/imageShow?imageid=[具体的图片id]

响应:

①:成功:

HTTP/1.1 200 OK

Content-Tpye:image/png

[图片的二进制内容]

②:失败

HTTP/1.1 200 OK

{

      ok:false,

      reason:"[具体的出错原因]"

}

3.源码的开发

1.数据库操作

先创建DBUtil,封装一下获取数据库链接的过程

dao--->数据访问层,这里面的类围绕着数据操作展开

ctrl+alt+t--->选中一些代码,在这些代码外面包上一些其他代码

二、实现代码

package dao;

public class Image {
    private int imageId;
    private String imageName;
    private int size;
    private String uploadTime;
    private String contentType;
    private String path;
    private String md5;

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }

    public String getImageName() {
        return imageName;
    }

    public void setImageName(String imageName) {
        this.imageName = imageName;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public String getUploadTime() {
        return uploadTime;
    }

    public void setUploadTime(String uploadTime) {
        this.uploadTime = uploadTime;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getMd5() {
        return md5;
    }

    public void setMd5(String md5) {
        this.md5 = md5;
    }

    @Override
    public String toString() {
        return "Image{" +
                "imageId=" + imageId +
                ", imageName='" + imageName + '\'' +
                ", size=" + size +
                ", uploadTime='" + uploadTime + '\'' +
                ", contentType='" + contentType + '\'' +
                ", path='" + path + '\'' +
                ", md5='" + md5 + '\'' +
                '}';
    }
}

1.封装数据库操作(DAO层)

DBUtil封装获取数据库链接的操作;

Image对应到一个图片对象(包含图片的相关属性);

ImageDao Image对象管理器,借助这个类完成Image对象的增删改查操作;

ImageDao有一个selectAll方法,查找出所有数据库中的数据,但是如果有上亿条数据这样查找很低效,更科学的方法可以指定一些其他筛选条件(分页);

  • 出现异常之后的具体措施:
  • 1.当前接触过的大部分都是打印调用栈
  • 2.让程序直接终止
  • 3.通过监控报警通知程序员

(JAR:类似于zip这样的压缩包,一大堆.class文件放在一起,打包成一个文件)

ImageDao:
package dao;
import common.JavaImageServerException;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ImageDao {
    //把image对象插入到数据库中
    public void insert(Image image)  {
        //1.获取数据库连接
        Connection connection=DBUtil.getConnection();
        //2.创建并拼装SQL语句
        String sql="insert into image_table values(null,?,?,?,?,?)";
        PreparedStatement statement=null;
        try {
            statement=connection.prepareStatement(sql);
            statement.setString(1, image.getImageName());
            statement.setInt(2,image.getSize());
            statement.setString(3,image.getUploadTime());
            statement.setString(4,image.getContentType());
            statement.setString(5,image.getPath());
            statement.setString(6,image.getMd5());

            //3.执行SQL语句
            int ret=statement.executeUpdate();
            if(ret!=1){
                //程序出现问题,抛出一个异常
                throw new JavaImageServerException("插入大户巨款出错");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }catch (JavaImageServerException e){
            e.printStackTrace();
        }finally{
            //4.关闭连接和statement对象
            DBUtil.close(connection,statement,null);
        }
    }

    //查找数据库中的所有图片的信息
    public List<Image> selectAll(){
        List<Image> images=new ArrayList<>();
        //1、获取数据库连接
        Connection connection=DBUtil.getConnection();
        //2.构造SQL语句
        String  sql="select * from image_table";
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        try {
            //3.执行sql语句
            statement=connection.prepareStatement(sql);
             resultSet=statement.executeQuery();
            //4.处理结果集
            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"));
                images.add(image);
            }
            return images;
        }catch (SQLException e){
            e.printStackTrace();
        }finally{
            //关闭连接
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    //根据imageId查找指定的图片信息
    public Image selectOne(int imageId){
        //1.获取数据库连接
        Connection connection=DBUtil.getConnection();
        //2.构造SQL语句
        String  sql="select * from image_table where imageId = ?";
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        //3.执行SQL语句
        try {
            statement=connection.prepareStatement(sql);
            statement.setInt(1,imageId);
            resultSet =statement.executeQuery();

            //4.处理结果集
            if(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"));
                return image;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            //5.关闭连接
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }
    //根据imageId删除指定图片
    public void delete(int imageId){
        //1、获取数据库连接
        Connection connection=DBUtil.getConnection();
        //2.拼装SQL语句
        String  sql="delete * from image_table where imageId = ?";
        PreparedStatement statement=null;
//        ResultSet resultSet=null;
        //3.执行SQL语句
        try {
            statement=connection.prepareStatement(sql);
            statement.setInt(1,imageId);
            int ret =statement.executeUpdate();

            //4.处理结果集
            if(ret!=1){
                throw new JavaImageServerException("删除数据库操作失败")
            }
        } catch (SQLException | JavaImageServerException e) {
            e.printStackTrace();
        }finally{
            //4.关闭连接
            DBUtil.close(connection,statement,null);
        }
    }


    public static void main(String[] args) {
        //用于进行简单的测试
        //1.测试插入数据
//        Image image=new Image();
//        image.setImageName("1.png");
//        image.setSize(100);
//        image.setUploadTime("2023");
//        image.setContentType("image/png");
//        image.setPath("./date/1.png");
//        image.setMd5("112233");
//        ImageDao imageDao=new ImageDao();
//        imageDao.insert(image);

        //2.测试查找所有图片信息
//        ImageDao imageDao=new ImageDao();
//        List<Image> images=imageDao.selectAll();
//        System.out.println(images);

        //3.测试查找指定图片信息
        ImageDao imageDao=new ImageDao();
        Image image=imageDao.selectOne(1);
        System.out.println(image);
        //4.测试查找指定图片信息
        ImageDao imageDao=new ImageDao();
        imageDao.delete(1);
    }
}

2.基于Servlet来搭建服务器

(1)安装Servlet

(2)创建一个类,继承HttpServlet父类(重写父类中的一些重要方法)

如果服务器收到的是GET方法,就会自动调用HttpServlet的doGet方法;

如果服务器收到的是POST方法,就会自动调用HttpServlet的doPost方法;

如果服务器收到的是DELETE方法,就会自动调用HttpServlet的doDelete方法;

HttpServletRequest req--->请求(方法、url、各种header、body)
HttpServletResponse resp--->响应(状态码、各种header、body)

给网页上显示一个hello world,修改请求还是修改响应--->修改响应;当代码写完之后,还需要修改webapp/WEB-INF/web.xml把新创建的Servlet注册进去:

<?xml version ="1.0" encoding =" UTF -8"?>

<web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation ="http://xmlns.jcp.org/xm1/ns/javaee
         http://xmIns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
        version ="3.1"
        metadata-complete =" true ">
  <servlet>
    <servlet-name>Hello</servlet-name >
    <servlet-class>HelloServlet </servlet-class>
  </ servlet >
  <servlet-mapping >
    <servlet-name> Hello </ servlet-name>
    <url-pattern >/ hello-servlet </url-pattern>
  </ servlet-mapping>
</web-app>

<servlet>标签-->>告诉Tomcat当前这个servlet对应到代码中的那个类

<servlet-mapping >标签-->>告诉Tomcat当前这个servlet对应到的URL的path是什么

三、基于Servlet搭建好项目的后端部分

部署:

(1)使用maven打包(war) maven package

(2)把war包拷贝到tomcat的webapps目录中

部署完之后,就可以在浏览器(http客户端)中访问服务器了。

http://47.98.116.42:8080/java_image_server/image

java_image_server----->war包的名字;image---->web.xml配置的内容

  • ImageServlet:

(1)上传图片

需要用到第三方库

对于JSON格式的响应,需要把content-type设定成指定格式

如果有两个图片,内容不同,但是名字相同,此时会出现上传失败的情况;应该让每次上传图片对应的路径都不相同(在路径上加上时间戳)

(2)查看所有图片信息

(3)查看所有指定图片信息

(4)删除指定图片

代码:

package api;

import dao.Image;
import dao.ImageDao;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class ImageServlet extends HttpServlet {
    //查看图片属性,既能查看所有,也能查看指定
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws SecurityException, ServletException, IOException {
        //考虑到查看所有图片属性和查看指定图片属性
        //通过是否URL中带有imageId参数来进行区分
        //存在imageId查看指定图片属性,否则就查看所有图片属性
        //例如:URL/image?imageId=100
        //imageId的值就是"100"
        //如果URL中不存在imageId那么返回null
        String imageId =req.getParameter("imageId");
        if(imageId==null || imageId.equals("")){
            //查看所有图片属性
            selectAll(req,resp);
        }else{
            //查看指定图片
            selectOne(imageId,resp);
        }

//        //req对象中包含了请求中的所有信息
//        //resp对象要生成的结果就放到里面去
//        //当前这个doGet方法就是要根据请求,生成响应
//        resp.setStatus(200);
//        //这个代码就是把hello这个字符串放到http响应的body中了
//        resp.getWriter().write("hello");
    }

    private void selectAll(HttpServletRequest req,HttpServletResponse resp){
        //1.创建一个ImageDao对象,并查找数据库
        ImageDao imageDao=new ImageDao();
        List<Image> images=imageDao.selectAll();
        //2.把查找到的结果转成JSON格式的字符串,并且写回到resp对象
        Gson gson=new GsonBuilder().create();
        //jsonData就是一个json格式的字符串,和之前约定的格式是一样的
        String jsonDate=gson.toJson(images);
        resp.getWriter().write(jsonDate);
    }
    private void selectOne(String imageId,HttpServletResponse resp){
        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);
    }

    //上传图片
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取图片的属性信息,并且存入数据库
        //1>需要创建一个factory对象,和upload对象,为了获取到图片属性做的准备工作
        FileItemFactory factory=new DiskFileItemFactory();
        ServletFileUpload upload=new ServletFileUpload(factory);
        //2>通过upload对象进一步解析请求
        List<FileItem> items=null;
        try{
            items=upload.paresRequest(req); 
        }catch(FileUploadException e){
            //出现异常说明解析出错
            e.printStackTrace();
            //告诉客户端出现的具体的错误是什么
            resp.setContentType("application/json;charset=utf-8");
            resp.getWriter().write("{\"ok\":false,\"reason\",\"请求解析失败\"}");
            return;
        }
        //3>把FileItem中的属性提取出来,转换成Image对象,才能存到数据库中
        //当前只考虑一张图片的情况
        FileItem fileItem=item.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());
        //TODO MD5(先不管,写死)
        image.setMD5("112233");
        //存到数据库中
        ImageDao imageDao=new ImageDao();
        imageDao.insert(image);

        //2.获取图片的内容信息,并且写入磁盘文件
        File file=new File(image.getPath());
        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}");
        resp.sendRedirect("index.html");
    }
    //删除指定图片
    @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\",\"image在数据库中不存在\"}");
            return;
        }
        //3.删除数据库中的记录
        imageDao.delete(Integer.parseInt(imageId));
        //4.删除本地磁盘文件
        File fule=new File(image.getPath());
        file.delete();
        resp.setStatus(200);
        resp.getWriter().write("{\"ok\":true}");
    }
}
  • ImageShowServlet

代码:

package api;

import dao.Image;
import dao.ImageDao;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

public class ImageShowServlet extends HttpServlet {
    @Override
    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.根据路径打开文件,读取其中的内容,写入到响应对象中
        resp.setContentType(image.getContentType());
        File file=new File(image.getPath());
        //由于图片是二进制文件,应该使用字节流的方式读取文件
        OutputStream outputStream=resp.getOutputStream();
        FileInputStream fileInputStream=new FileInputStream(file);
        byte[] buffer=new byte[1024];
        while(true){
            int len=fileInputStream.read(buffer);
            if(len==-1){
                //文件读取结束
                break;
            }
            //此时语句读到一部分数据,放到buffer里面,把buffer中的内容写到响应对象中
            outputStream.write(buffer);
        }
        fileInputStream.close();
        outputStream.close();
    }
}

四、实现前端页面(展示图片,增删操作)、

  • 实现前端页面主要应用的技术:

(1)HTML-->网页的骨架;

(2)CSS--->描述网页上组件的样式(位置、大小、颜色等);

(3)JavaScript:描述前端页面上的一些动作(和用户具体交互的行为)

(HTML、CSS、JavaScript可以写到一个HTML文件中,也可以分开写)

把网页上显示的这些预览图片替换成服务器上保存的图片,img标签中scr改成服务器上存在的图片的url就可以了;

需要获取到服务器上所有图片的url(ImageServlet),需要通过JS先获取到所有图片的属性,再分别加载每一个图片;使用JS来完成。

  • 主流的前端框架

(1)Vue

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 
var app = new Vue({ 
  el: '#app', 
  data: { 
    message: 'Hello Vue!' 
  } 
})

(2)React(facebook)

(3)Auglar(google)

JS中变量类型都是在初始化的时候自动推导的

var声明这是一个变量;

Vue所做的最核心的工作,把页面显示的内容和JS中的代码相互关联到一起;

修改js中的变量就能很方便的影响的页面的显示情况。

var app = new Vue({ 
    el:'#app', 
    data: { 
        images: [ 
          { 
            imageId: 1, 
            imageName: "1.png", 
            contentType: "image/png", 
            md5: "aabbccdd", 
          }, 
          { 
            imageId: 2, 
            imageName: "2.png", 
            contentType: "image/png", 
            md5: "aabbccdd", 
          } 
        } 
    }, 
    methods: {
 
    }, 
});

Vue对象中构造一组写死的数据,先去借助这组数据来渲染页面

Vue中的命令:

1>v-for--->>循环访问一个数据

2>v-bind-->>把数据绑定到html标签上的某个属性

<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"> 
         <img v-bind:src=" 'imageShow?imageId=' + image.imageId " 
         style="height:200px;width:200px"> 
       <h3>{{image.imageName}}</h3> 
     </div> 
   </div> 
 </figure> 
</div>

3>v-on-->>绑定某种事件的处理函数(点击鼠标、双击、右键等)

remove(image_id) { 
    $.ajax({ 
        url:"image?image_id=" + image_id, 
        type:"delete", 
        context: this, 
        success: function(data, status) { 
            this.getImages(); 
            alert("删除成功"); 
        } 
    }) 
}

如果是在标签内部使用Vue对象中的数据,就需要使用插值表达式;

如果是在标签属性中使用Vue对象中的数据,步需要使用插值表达式,但要搭配Vue的命令;

images这个数组里面的内容是写死的,接下里通过浏览器中JS代码请求服务器,获取到服务器上都有那些图片,把这个数据作为Vue渲染的依据(Vue对象中的images数组)

getImages() { 
    $.ajax({ 
      url: "image", 
      type: "get", 
      context: this, 
      success: function(data, status) { 
          this.images = data; 
          // 这个代码用来触发 resize 事件. 
          $("#app").resize(); 
      } 
   }) 
},

ajax:JSz中构造HTTP请求发送给服务器的一种实现方式

原来页面的渲染过程:

1>先加载图片

2>再根据图片大小设定图片的位置,设定显示图片的空间大小

使用ajax来渲染:

1>页面先尝试获取页面的大小,并设定显示图片的空间(当前图片还没有获取到,不知道图片的大小)

2>通过ajax获取图片内容

改进上传操作,上传成功之后,自动跳转到主页index.html  HTTP重定向

实现删除图片,浏览器给服务器发送一个DELETE/image?imageId=xxx这样的请求就可以了(ajax完成)

拓展:

(1)简单的防盗链机制(通过一定的机制来限制其他人来使用图片)

可以判定当前请求的referer爱段(HTTP请求协议中的header部分),是不是在我们代码指定的白名单中,如果是,就可以访问。

referer-->表示当前请求的上一个页面的地址

再代码中用一个hashSet存一下允许访问的Referer就可以了,展示图片的时候判断一下是否再hashSet中存在即可。

(2)优化磁盘空间

应用到MD5,如果两个图片内容完全一样,就在磁盘上存一份就可以了。通过MD5可以判断两个图片内容是否一样。

图片文件虽然是二进制数据,但是本质上也是字符串,针对图片内容计算MD5。

如果两个图片内容相同,得到的MD5一定是相同的,反之,近似认为MD5相同,原图内容一定相同。

理论上使是有可能两个图片的内容不同,MD5相同,但实际上出现概率极低(MD5自身算法设计上引起的特性)

  • MD5的特点:
  • (1)不管原串多长,得到的MD5值是一个固定长度;
  • (2)原串哪怕变动一点点(一个字节),MD5值就会变动很大;
  • (3)计算MD5值的过程很简单,但是通过MD5值无法推测出原字符的;

实现思路:

上传图片的时候,先判定新图片的MD5在数据库中是否存在,如果已经存在了,就不用把图片内容写到磁盘上,如果不存在就写到磁盘上。

1>该上传代码的逻辑,磁盘文件名用md5值来表示

2>修改ImageDao新增一个接口,能按照MD5查找数据库内容

3>修改上传图片的逻辑,根据MD5判定当前图片是是否要写磁盘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1007259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在ubuntu18.04上编译C++版本jsoncpp/opencv/onnxruntime且如何配置CMakelist把他们用起来~

这篇文章背景是笔者在ubuntu上编译C代码&#xff0c;依赖一些包&#xff0c;然后需要编译并配置到CMakelist做的笔记。主要也是一直不太懂CMakellist&#xff0c;做个笔记以防忘记&#xff0c;也给读者提供一站式的参考&#xff0c;可能您需要的不是这几个包&#xff0c;但大同…

STM32f103入门(12)USART串口信息发送+接收

USART 介绍串口发送使用工具初始化发送数据接收数据 介绍 电平标准是数据1和数据0的表达方式&#xff0c;是传输线缆中人为规定的电压与数据的对应关系&#xff0c;串口常用的电平标准有如下三种&#xff1a; TTL电平&#xff1a;3.3V或5V表示1&#xff0c;0V表示0 RS232电平&…

成都都市圈公共图书馆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著

成都都市圈公共图书馆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著

Discrod账号为什么被封?怎么解封?

Discord作为海外社交产品的新晋王者&#xff0c;近两年来非常受欢迎&#xff0c;据统计&#xff0c;每个月使用Discord的用户数超过3000万。而在跨境电商领域&#xff0c;”内容社群”的打法已经见怪不怪&#xff0c;营销推广少不了Discord&#xff0c;拥有一个或者多个成熟的D…

深度学习-激活函数

文章目录 基础知识Sigmoid函数 &#xff08;Logistic函数&#xff09;双曲正切函数&#xff08;Tanh函数&#xff09;线性整流函数&#xff08;ReLU函数&#xff09;Leaky ReLU函数Softmax函数 基础知识 激活函数是神经网络中的一种非线性函数&#xff0c;它作为神经元的输出函…

Linux 安装Harbor镜像仓库私服

参考链接 Docker 的基础知识、安装、使用Harbor镜像仓库私服搭建 Harbor是什么&#xff1f; Harbor是由VMware公司开源的企业级的Docker Registry管理项目&#xff0c;它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。 Harbor 的所有组…

WebGL正射投影

目录 可视范围&#xff08;正射类型&#xff09; 可视空间 正射投影的盒状可视空间的工作原理 盒状可视空间 定义盒状可视空间 Matrix4.setOrtho&#xff08;&#xff09; 按键控制near、far ​编辑 示例效果 示例代码 代码详解 修改near和far值 通过右方向键增大n…

从物理叠加到化学反应,看方太如何把洗碗机玩出「新价值」

文 | 智能相对论 作者 | 佘凯文 随着酷暑烈日的逐渐远去&#xff0c;秋意也开始浓厚了起来。所谓“秋风起&#xff0c;秋膘贴”&#xff0c;在“金九银十”的当下&#xff0c;美食自是不可辜负的恩赐。肥美多膏的大闸蟹、软糯香甜的南瓜羹、爽脆可口的莲藕汤..... 秋季的美食…

idea中启动maven项目报错-java: 程序包lombok.extern.slf4j不存在问题如何解决

1、 现象&#xff1a; 在springboot的maven项目启动时&#xff0c;报错&#xff1a; Error:(3, 27) java: 程序包lombok.extern.slf4j不存在 编译不报错&#xff0c;maven依赖也合适&#xff0c;项目就是无法启动 原因&#xff1a; 其实不是项目本身或者maven本身的问题&am…

Java垃圾收集机制

目录 前言 判断对象是否存活 引用计数算法 可达性分析算法 GC Root的产生 Java中的四种引用类型 1.强引用 强引用弱化方式 方式1&#xff1a;使对象指向null 方式2&#xff1a;使对象超出作用域范围 2.软引用 3.弱引用 4.虚引用 垃圾收集算法 分代收集理论 垃圾…

Linux内核及可加载内核模块编程

图1 Linux系统整体结构 图2 Linux的源代码结构 下面显示一段内核模块代码案例&#xff1a; #include <linux/moduLe.h> #include <linux/kernel.h #include <linux/intt.h> /*模块的初始化函数lkp_ init()_init是用于初始化的修饰符 */ static int __init lk…

第十五届全国大学生数学竞赛报名快要截止了,你报上名了吗?

关于组织参加 第十五届全国大学生数学竞赛的通知 01 为了培养人才、服务教学、提高大学生学习数学的兴趣&#xff0c;培养学生分析问题、解决问题的能力&#xff0c;发现和选拔数学创新人才&#xff0c;为学生提供一个展示基础知识和思维能力的舞台&#xff0c;我校决定组织参…

SSM整合demo及个人思考

SSM整合 项目整体架构说明1. 创建Maven项目2. 配置web.xml4. 配置springmvc.xml5. 配置spring.xml6. 配置mybatis-config.xml以及创建mapper接口和mapper配置文件7. 配置log4j.xml8. 后端CURD测试8.1 在数据库中插入数据8.2 pojo中的实体类Employee8.3 mapper层的EmployMapper接…

oppo手机便签隐藏了一条怎样打开?手机如何找到隐藏便签?

有不少用户在使用OPPO手机的过程中&#xff0c;遇到了一些问题&#xff0c;例如自己在使用手机系统便签应用时&#xff0c;把一条重要的便签设置了隐藏&#xff0c;但是现在找不到隐藏的便签了。 那么oppo手机便签隐藏了一条怎样打开&#xff1f;OPPO手机如何找到隐藏便签&…

【C++】基础知识点回顾 上:命名空间与输入输出

前言 学习C一段时间后&#xff0c;再回过头来看这些C的基础知识&#xff0c;感觉有很多细节是自己当时没有注意的&#xff0c;所以写一篇文章来回顾复习一下C的基础知识。 命名空间的使用 相信很多朋友在学习C的第一个代码的时候&#xff0c;在写上头文件之后&#xff0c;紧…

运营商大数据实时获取精准数据

随着大数据技术的快速发展和完善&#xff0c;出现了一种新的扩张方式——互联网大数据的精准扩张。如果没有一个好的渠道来获得顾客&#xff0c;这就像准备热情地做饭&#xff0c;但当饭吃完后&#xff0c;人们只能饿了。 今天的消费者已经从最早的线下消费逐渐过渡到互联网消…

浅谈6种API架构模式

在摸鱼刷X时&#xff0c;看到一张非常棒的图&#xff0c;是关于不同API架构的&#xff0c;下面学习记录一下。 &#xff08;摘自网络&#xff09; 1、gRPC gRPC是一种高性能、跨语言、易扩展的远程过程调用(RPC)框架&#xff0c;可用于分布式系统之间的通信。gRPC被广泛地应用…

金融贷款行业实时高精准获客 ——三网运营商大数据

都说生产是第一因素&#xff0c;但对于任何企业来说&#xff0c;客户来源才是第一因素。 在大多数行业&#xff0c;获得客户的困难已经成为行业的挑战。如今&#xff0c;许多行业和企业获得客户的主要来源是在线促销和客户获取。现在几乎每个人都有一部手机。运营商可以根据移…

每日一练 | 网络工程师软考真题Day33

阅读以下说明&#xff0c;答复以下【问题1】和【问题2】 【说明】 某单位内部网络拓扑结构如图5-1所示&#xff0c;在该网络中采用RIP路由协议。 【问题1】 1&#xff0e;路由器第一次设置时&#xff0c;必须通过Console口连接运行终端仿真软件的微机进行配置&#xff0c;此时…

ByteV联合“智农”打造--数字孪生大棚可视化

ByteV联合“智农”打造的数字孪生大棚可视化&#xff0c;不仅要让粮食稳产、增产&#xff0c;更要对土壤肥力进行改良和提升。不仅能够实现科技引领农业发展&#xff0c;更在智慧农业的基础上实现一站式托管&#xff0c;真正做到技术提升、5G引领、建后管护的闭环管理。让高标准…