图片服务器(图床)的设计与实现

图片服务器(图床)的设计与实现

一.项目介绍

图片服务器的核心功能:显示图片,上传图片,删除等。

应用场景:将图片上传到另外的服务器上,并提供链接以在其他网站分享图片、论坛、博客引用。现有的图片服务器比如阿里云OSS、腾讯云、TG图床 (IMG.TG)、聚合图床等。
index.htmlindex.html

二、​核心内容:

基础的Web服务器设计开发(Servlet),Servlet是Tomcat这个HTTP服务器所提供的一组编程接口

使用数据库(MySQL)的JDBC操作MySql

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

前后端交互接口设计(基于HTTP协议)

使用Java中的Gson库操作JSON数据

测试HTTP服务器(Postman)

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

三.服务器设计

3.1数据库设计

MySql本质上是一个服务器程序,在安装时,既安装了本地服务器,也安装了客户端。在MySql数据库中存储的是图片的属性(元信息)即图片正文,以文件的形式直接存在磁盘上的,数据库中每次就记录一个path,path就对应到磁盘上的一个文件。

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

图片表的设计:
图片表的设计

实体类设计(将数据库的表转为类,字段转为成员属性一一对应):

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 + '\'' +

                '}';

    }



}

​3.2.服务器API设计

3.2.1.JSON

一种数据组织的格式,格式键值对的结构。JSON只是一种数据格式,和编程语言无关。使用JSON完成数据的序列化方便完成网络传输。

3.2.2.Gson:一个开源的JSON解析库

3.2.3.使用form表单在HTML中完成文件上传操作

3.3.前后端交互设计

3.3.1.新增图片请求:使用POST,POST到/image这个路径上(包含图片自身的属性)
上传图片请求
3.3.2.查看所有图片信息
查看所有图片信息
3.3.3.查看指定图片属性
查看指定图片属性

3.3.4.删除指定图片
删除指定图片
3.3.5.查看指定图片内容
查看指定图片内容

四、项目后端的实现

4.1数据库操作

4.1.1封装数据库连接池

使用DBUtil封装获取数据库链接,Image对应每一个图片的属性。

代码如下:

代码如下(示例):

package dao;//数据访问层



import com.mysql.cj.jdbc.MysqlDataSource;



import javax.sql.DataSource;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;



public class DBUtil   {

    private  static final String  URL = "jdbc:mysql://127.0.0.1:3306/java_image_sever?characterEncoding=utf8&useSSL=true";

    private static final String USERNAME = "root";

    private static final String PASSWORD = "";



    private static volatile DataSource dataSource = null;



    public static DataSource getDataSource() {

//        通过这个方法创建 DataSource 示例

        if (dataSource == null){

        synchronized (DBUtil.class) {

            if (dataSource == null) {

                dataSource = new MysqlDataSource();

                MysqlDataSource tmpDataSource = (MysqlDataSource) dataSource;

                tmpDataSource.setURL("jdbc:mysql://localhost:63342/java_image_server");

                tmpDataSource.setUser("root");

                tmpDataSource.setPassword("123456");

             }

             }

        }

            return dataSource;

    }

    public static Connection getConnection()   {

        try {

            return getDataSource().getConnection();

        } catch (SQLException e) {

            e.printStackTrace();

        }

        return null;

    }

    public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet)  {

//        顺序,先创建的先关闭

        try {

        if (resultSet != null) {

                resultSet.close();

            }



            if (preparedStatement != null) {

                preparedStatement.close();

            }



                if (connection != null) {

                connection.close();

                }

          }

         catch (SQLException e) {

            e.printStackTrace();

        }

    }

}

4.1.2数据访问层,实现数据的操作,ImageDao类完成Image对象的增删改查操作
代码如下:

package dao;



import com.sun.javafx.scene.input.InputEventUtils;



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 对象插入到数据库中

     * @param image

     */

    public  void  insert(Image image){

//        1.获取数据库链接

        Connection connection = DBUtil.getConnection();

//        2.创建并且拼接 SQL语句

        String sql = " insert into image_table values(null,?,?,?,?,?,?)";

        PreparedStatement preparedStatement = null;

        try {

            preparedStatement = connection.prepareStatement(sql);

            preparedStatement.setString(1, image.getImageName());

            preparedStatement.setInt(2,image.getSize());

            preparedStatement.setString(3,image.getUploadTime());

            preparedStatement.setString(4,image.getContentType());

            preparedStatement.setString(5, image.getPath());

            preparedStatement.setString(6,image.getMd5());



            //        3.执行 SQL语句

            int ret = preparedStatement.executeUpdate();

            if (ret != 1){

//                如果程序出现问题了,就抛出异常

                    throw new JavaImageServerException("插入数据错误");

            }



/*

            DBUtil.close(connection,preparedStatement,null);

*/



        } catch (SQLException | JavaImageServerException e) {

            e.printStackTrace();

        }finally {

            //        4.关闭连接和释放 statement对象 ,close代码有概率执行不到(超出异常)

            DBUtil.close(connection,preparedStatement,null);



        }



    }



    /**

     *  查找数据库中的所有图片的信息

     * @return

     */

    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;

        //3.执行 SQL语句

        try {

            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 {

            //5.关闭连接

            DBUtil.close(connection,statement,resultSet);



        }

            return null;

    }



    /**

     * 根据 imageId 查找指定图片的操作

     * @param imageId

     * @return

     */

    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;

        try {

            //3.执行 SQL语句

            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 删除指定的图片

     * @param imageId

     */

    public void delete(int imageId){

        //1.获取数据库连接

        Connection connection = DBUtil.getConnection();

        //2.拼装 SQl语句

        String sql = "";

        PreparedStatement statement = null;

        //3.执行SQL语句

        try {

            statement = connection.prepareStatement(sql);

            statement.setInt(1,imageId);

            int ret = statement.executeUpdate();

            if (ret != 1){

                throw new JavaImageServerException("删除数据库操作失败");

            }

        } catch (SQLException | JavaImageServerException e) {

            e.printStackTrace();

        } finally {

            //4.关闭连接

            DBUtil.close(connection,statement,null);

        }



    }



//      数据库不在本地时,程序无法访问(打一个jar包拷贝到云服务器上再执行

    public static void main1(String[] args) {

//        用于简单的测试

//        1.测试插入数据

        Image image = new Image();

        image.setImageName("1.jpg");

        image.setSize(100);

        image.setUploadTime("20");

        image.setContentType("image/png");

        image.setPath("./data/1.png");

        image.setMd5("11223344");

        ImageDao imageDao = new ImageDao();

       imageDao.insert(image);

    }

    //2.测试查找所有图片

    public static void main2(String[] args) {

        ImageDao imageDao = new ImageDao();

        List<Image> images = imageDao.selectAll();

        System.out.println(images);

    }

    //3.测试查找指定图片

    public static void main3(String[] args) {

        ImageDao imageDao = new ImageDao();

        Image image = imageDao.selectOne(1);

        System.out.println(image);

    }



    public static void main(String[] args) {

        //4.测试删除图片

        ImageDao imageDao = new ImageDao();

        imageDao.delete(1);

    }

}

4.2.服务器设计

4.2.1 安装Servlet,基于Servlet来搭建服务器

4.2.2 创建继承HttpServlet类的子类

重写父类中的一些重要方法,如doGet方法、doPost方法、doDelete方法等。

ImageServlet:

package api;



import com.google.gson.Gson;

import com.google.gson.GsonBuilder;

import dao.Image;

import dao.ImageDao;

//import org.apache.commons.codec.digest.DigestUtils;

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.FileItemFactory;

import org.apache.commons.fileupload.FileUploadException;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;



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 {

    /**

     * 查看图片属性: 既能查看所有, 也能查看指定

     * @param req

     * @param resp

     * @throws ServletException

     * @throws IOException

     */

    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws 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);

        }

    }



    private void selectAll(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        resp.setContentType("application/json; charset=utf-8");

        // 1. 创建一个 ImageDao 对象, 并查找数据库

        ImageDao imageDao = new ImageDao();

        List<Image> images = imageDao.selectAll();

        // 2. 把查找到的结果转成 JSON 格式的字符串, 并且写回给 resp 对象

        Gson gson = new GsonBuilder().create();

        //    jsonData 就是一个 json 格式的字符串了, 就和之前约定的格式是一样的了.

        //    重点体会下面这行代码, 这个方法的核心, gson 帮我们自动完成了大量的格式转换工作

        //    只要把之前的相关的字段都约定成统一的命名, 下面的操作就可以一步到位的完成整个转换

        String jsonData = gson.toJson(images);

        resp.getWriter().write(jsonData);

    }



    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);

    }

    /**

     * 上传图片

     * @param req

     * @param resp

     * @throws ServletException

     * @throws IOException

     */

    @Override

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 1. 获取图片的属性信息, 并且存入数据库

        //  a) 需要创建一个 factory 对象 和 upload 对象, 这是为了获取到图片属性做的准备工作

        //     固定的逻辑

        FileItemFactory factory = new DiskFileItemFactory();

        ServletFileUpload upload = new ServletFileUpload(factory);

        // b) 通过 upload 对象进一步解析请求(解析HTTP请求中奇怪的 body 中的内容)

        //    FileItem 就代表一个上传的文件对象.

        //    理论上来说, HTTP 支持一个请求中同时上传多个文件

        List<FileItem> items = 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;

        }

        //  c) 把 FileItem 中的属性提取出来, 转换成 Image 对象, 才能存到数据库中

        //     当前只考虑一张图片的情况

        FileItem fileItem = items.get(0);

        Image image = new Image();

        image.setImageName(fileItem.getName());

        image.setSize((int)fileItem.getSize());

        // 手动获取一下当前日期, 并转成格式化日期, yyMMdd => 20200218

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");

        image.setUploadTime(simpleDateFormat.format(new Date()));

        image.setContentType(fileItem.getContentType());

        // MD5 暂时先不去计算

        image.setMd5(DigestUtils.md5Hex(fileItem.get()));

        // 自己构造一个路径来保存, 引入时间戳是为了让文件路径能够唯一

        image.setPath("./image/" + image.getMd5());

        // 存到数据库中

        ImageDao imageDao = new ImageDao();



        // 看看数据库中是否存在相同的 MD5 值的图片, 不存在, 返回 null

//        Image existImage = imageDao.selectByMd5(image.getMd5());



        imageDao.insert(image);



        // 2. 获取图片的内容信息, 并且写入磁盘文件

        if (existImage == null) {

            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");

    }



    /**

     * 删除指定图片

     * @param req

     * @param resp

     * @throws ServletException

     * @throws IOException

     */

    @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 }");

    }

}

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;

import java.util.HashSet;



public class ImageShowServlet extends HttpServlet {

    static private HashSet<String> whiteList = new HashSet<>();



    static {

        whiteList.add("http://47.98.116.42:8080/java_image_server/index.html");

    }



    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String referer = req.getHeader("Referer");

        if (!whiteList.contains(referer)) {

            resp.setContentType("application/json; charset: utf-8");

            resp.getWriter().write("{ \"ok\": false, \"reason\": \"未授权的访问\" }");

            return;

        }



        // 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();

    }

}

五、前端页面的实现

5.1主要应用的技术:

网页的骨架 --> HTML ;

描述网页上组件的样式 —> CSS ;

前端页面上的和用户具体交互的行为 —> JavaScript:

5.2主流的前端框架

5.2.1 有React,Auglar,Vue等。其中Vue.js更容易上手,格式如下:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

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: {



    },

});

5.2.2 在Vue对象中构造一组数据来渲染页面
页面的渲染过程

<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>

处理函数:

remove(image_id) {

    $.ajax({

        url:"image?image_id=" + image_id,

        type:"delete",

        context: this,

        success: function(data, status) {

            this.getImages();

            alert("删除成功");

        }

    })

}

5.3拓展:

5.3.1图片防盗链机制

限制其他人来使用图片,判定当前请求的referer字段(表示当前请求的上一个页面的地址),代码中用一个hashSet存一下允许访问的Referer,展示图片的时候判断是否在hashSet中存在,如果发起的页面在代码指定的白名单(一个数组/列表)中,就可以访问。

5.3.2使用MD5优化磁盘空间

MD5的特点:不管原串多长,得到的MD5值都是一个固定长度,哪怕变动一点点,MD5值就会变动很大。MD5值的计算很简单,但是通过无法通过MD5值推测出原字符。

实现思路:

通过MD5判断两个图片内容是否一样:图片文件是二进制数据,本质上也是字符串,使用MD5计算图片内容,如果两个图片内容相同,得到的MD5是相同的,如果MD5近似相同,原图内容也是相同,这是MD5自身在算法设计上引起的特性,即使是有可能存在两个图片的内容不同,MD5相同,但实际上出现概率极低。这样知道了两个图片内容完全一样,在磁盘上存一份即可。在上传图片的时候,判定新图片的MD5在数据库中是否存在,如果存在就不再把图片内容写到磁盘上,不存则就写到磁盘上。

六、测试用例

6.1功能测试

6.1.1上传功能:

测试是否支持多种图片格式(如JPEG、PNG、GIF等)的上传。
测试上传文件的大小限制,确保超过限制的图片无法上传,并显示合适的错误提示。
测试上传过程中是否支持中断重传,以及上传进度的显示是否正确。 验证上传后的图片能否正常显示,且与原图一致。

6.1.2查看功能:

检查用户是否能够正常查看已上传的图片,包括缩略图和原图。
测试图片的放大、缩小、旋转等基本操作是否顺畅。
验证图片的加载速度,确保在合理时间内完成加载。

6.1.3删除功能:

测试删除操作是否成功,且删除后的图片不再显示。
验证删除操作是否会影响其他图片的正常显示。
检查删除操作后,数据库中的相关记录是否也被正确删除。

6.2性能测试

6.2.1负载测试:

在高并发情况下,测试服务器的响应时间、吞吐量和错误率。
验证服务器在大量图片上传、查看和删除操作下的性能表现。

6.2.2压力测试:

模拟大量用户同时访问服务器,测试其稳定性和可靠性。
检查在极端情况下,服务器是否会出现崩溃或性能下降的情况。

6.3界面测试

6.3.1布局测试:

检查界面布局是否合理,元素排列是否整齐。
验证图片显示区域的尺寸是否适应不同分辨率的屏幕。

6.3.2交互测试:

测试用户与界面的交互是否顺畅,如按钮点击、鼠标悬停等。
验证图片加载失败时,界面是否显示合适的错误提示

6.4兼容性测试

6.4.1浏览器兼容性:

在不同浏览器(如Chrome、Firefox、Safari等)中测试图片服务器的功能是否正常。
检查在不同浏览器版本中,界面的显示效果是否一致。

6.2.2操作系统兼容性:

在不同操作系统(如Windows、macOS、Linux等)上测试图片服务器的性能表现。
验证在不同操作系统中,图片的加载和显示是否正常。

6.5易用性测试

6.5.1操作流程:

检查用户上传、查看和删除图片的操作流程是否简洁明了。
验证用户在使用过程中是否容易上手,无需查阅帮助文档。

6.5.2错误提示:

测试在操作过程中遇到错误时,系统是否提供明确的错误提示和解决方案。
验证错误提示的语言是否友好、易于理解。

6.6安全测试

6.6.1数据安全:

检查上传的图片是否经过必要的安全性处理(如防篡改、防病毒等)。
验证存储的图片数据是否加密存储,以防止数据泄露。

6.6.2权限控制:

测试不同用户角色的权限设置是否合理,如管理员和普通用户的权限差异。
验证用户在未经授权的情况下,无法执行敏感操作(如删除其他用户的图片)。

6.6.1防御攻击:

检查服务器是否具备防御常见网络攻击的能力(如SQL注入、跨站脚本攻击等)。
验证服务器在面对恶意请求时,是否能够正常处理并保护用户数据安全。

脑图

图片服务器(图床)测试用例

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值