图片服务器项目

项目源码:https://github.com/fjm1220/image_server/tree/master/src/main/java

项目简介:

该项目是基于JavaServlet构建的一个图片服务器,主要实现了图片的上传,删除指定图片和展示功能。将图片上传到图片服务器上就可以在其他网站通过url引用该图片了(GitHub,博客)。

项目的核心知识点:

1.web服务器的设计和开发(servlet)
  Servlet是Tomcat这个HTTP服务器所提供的一组编程接口,使用Servlet这一套编程接口可以开发自己的业务逻辑,并部署到Tomcat上即可搭建自己的服务器。
2.使用数据库(MySQL)进行存储数据;JDBC操作数据库
3.数据库设计(根据实际场景设计数据库表结构)
4.前后端交互的API设计(基于HTTP协议)
5.JSON数据格式,使用Java中的Gson这个库操作JSON数据。(进行API设计时使用)
6.使用postman测试HTTP服务器
7.使用HTML,CSS,JavaScript技术构建一个简单的网页

服务器设计

项目的数据库设计:

数据库中存储图片的属性
图片正文以文件的形式直接存放在磁盘上,数据库中记录的每一个path字段就对应磁盘上的文件。

create table Image_table(
image_id int not null primary key auto_increment,
image_name varchar(50),
size int,
uploadTime varchar(50),
conntenType varchar(50),
md5 varchar(1024),
path varchar(1024)
);

在这里插入图片描述

MD5:消息摘要算法,是一种密码散列函数,将数据运算成另一固定长度值,是散列算法的基础原理,可以产生出一个128为(16字节)的散列值,用于确保信息传输完整一致。
MD5特点:
(1)对于任意长度的数据,算出的MD5值长度都是固定的
(2)对于元数据计算出MD5值很容易
(3)对原数据进行任何改动,哪怕只修改一个字节,所得到的MD5值都有很大的区别。
(4)想找到两个不同的数据,使他们具有相同的MD5值非常困难。
在本项目中进行MD5的校验,进行磁盘空间优化,使相同内容的图片在磁盘上只保存一份。
服务器API设计(前后端交互接口设计)

这一步的设计实际上就是说HTTP具体要构造成什么样子。我们可以先写一个简单的html文件来上传图片,然后用fiddler进行抓包,查看上传图片的http请求长什么样子。

<html>
<head>
</head>
<body>
<form id="upload-form" action="image" method="post" enctype="multipart/form-data" >
        <input type="file" id="upload" name="upload" /> <br />
        <input type="submit" value="Upload" />
</form>
</body>
</html>

具体设定的接口如下:
1.上传图片

//请求:
POST/image
Content-Type:multipart/form-data;
[图片正文的二进制内容]
//响应
//1.上传成功:
HTTP/1.2 200 OK
{
"ok":true,
}
//2.上传失败
HTTP1.1 200 OK
{
"ok":false
"reson":"具体的失败原因"
}

在这里不管成功还是失败都返回"200 OK",不同的是当成功时响应body中的"ok"为true,失败时body中"ok"为false,并且返回具体失败原因,这些利用json进行组织。

JSON:一种数据组织格式,格式是键值对的格式
JSON只是一种数据格式,和编程语言无关。该项目中使用JSON完成数据的序列化,方便进行网络传输。
Gson:是一个开源的JSON 解析库。

2.查看所有图片信息(数据库中存的图片属性)

//请求
GET/image
//响应
//1.成功
//数组中存放的是json数据格式的
HTTP/1.1 200 OK
[
{
image_id:1,
image_name:"image/png",
size:1000,
uploadTime:"20200222",
path:"./data/1.png"
md5:"11223344",
},
{
........
}
]
//2.获取失败:直接返回空数组
HTTP/1.1 200 OK
[]

上传图片用的是POST方法,而查看图片用的是GET方法;这两者的区别就在于,GET不带请求正文,而POST带请求正文。
具体区别如下:

1、
(1)Get请求方式将请求信息放在URL后面,请求信息和URL之间以?隔开,请求信息的格式为键值对;由于请求信息放置在URL中因此请求报文中不需要请求体。
(2)Post请求方式将请求信息放置在报文体中,想获取请求信息必须解析报文。所以请求报文中含有请求体。
2、
(1)由于Get请求信息放置在URL中,因此是有长度限制的,因为URL本身是有长度限制的。URL的长度有浏览器和服务器决定的。
(2)Post请求信息放置在报文体中,数据长没有长度限制。
3、
(1)Get是读取一个资源,可以将Get数据缓存在浏览器。
(2)Post是提交一个表单,不能缓存,如果尝试重新执行Post请求,浏览器会弹出提示框询问是否重新提交表单。

3.查看指定图片属性

//请求
GET/image?image_id=[具体的id值]
//1.成功
HTTP/1.1 200 OK
{
image_id:1,
image_name:"image/png",
size:1000,
uploadTime:"20200222",
path:"./data/1.png"
md5:"11223344",
}
//2.失败
HTTP/1.1 200 OK
{
ok:false,
reson:"具体出错原因"
}

4.删除指定图片

//请求
DELETE/image?image=[具体的图片id] 
//响应
//1.成功
HTTP/1.1 200 OK
{
ok:true,
}
//2失败
HTTP/1.1 200 OK
{
ok:false,
reson:"具体出错原因"
}

删除操作不一定非得用DELETE方法,例如:GET/image?image_id =xxx&delete=1
5.查看指定图片内容

//请求
GET/imageShow?imageId=[具体图片id]
//响应
//1.成功
HTTP/1.1 200 OK
Content-Type:image/png 
[图片的二进制内容]
//2.失败
HTTP/1.1 200 OK 
{
ok:false,
reson:"[具体的出错原因]"
}

源码实现(部分)

1.数据库操作
创建一个DBUtil类封装获取数据库连接的操作,用来进行辅助数据库连接。
创建一个Image类,对用一个图片对象(包含图片的属性信息)
创建一个ImageDao类,作为对象的管理器

public class DBUtil {
    //3306后面跟的是数据库的名字
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/image_server?characterEncoding=utf8&useSSL=true";
    private static final String USERNAME = "root";
    //此处密码是我随便写的
    private static final String PASSWORD = "XXXXXX";
    private static DataSource dataSource = null;
    public static DataSource getDataSource(){
        //通过这个方法来创建DataSource的实例
        //懒汉模式
        //保证线程安全:
        //1、先加锁,加锁低效  2、二次判断  3、volatile保证内存可见性
        if(dataSource == null){
            synchronized (DBUtil.class){
                if(dataSource == null){
                    dataSource = new MysqlDataSource();
                    MysqlDataSource tempDataSource = (MysqlDataSource) dataSource;
                    tempDataSource.setURL(URL);
                    tempDataSource.setUser(USERNAME);
                    tempDataSource.setPassword(PASSWORD);
                }
            }
        }
        return dataSource;
    }
    public static Connection getConnection(){
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
       //注意关闭顺序,先连接的后关闭
        try {
            if(resultSet != null){
                resultSet.close();
            }
            if(statement != null){
                statement.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

ImageDao:(以上传图片为例)
1.上传图片

 public void insert(Image image){
        //1.获取数据库连接
        Connection connection = DBUtil.getConnection();
        //2.创建片拼接SQL语句
        PreparedStatement statement = null;
        try {
            String sql = "insert into Image_table values(null,?,?,?,?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,image.getImage_name());
            statement.setInt(2,image.getSize());
            statement.setString(3,image.getUploadTime());
            statement.setString(4,image.getContentType());
            statement.setString(5,image.getMd5());
            statement.setString(6,image.getPath());
            //3.执行SQL语句
            int ret = statement.executeUpdate();
            if(ret != 1){
                //程序出现问题,抛出一个异常
                throw new ImageServerException("插入数据库错误");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ImageServerException e) {
            e.printStackTrace();
        }finally {
            //4.关闭连接和statement对象
            DBUtil.close(connection,statement,null);
        }
    }

2.查看全部图片属性信息

public List<Image> selectAll();   //返回一个List
用到的数据库语句:select * from Image_table;

3.根据image_id查看指定图片

public Image selectOne(int image_id);   //传入image_id 参数
用到的数据库语句:select * from Image_table where image_id = ?;

4.删除指定图片

public void delete(int image_id)
用到的数据库语句:delect from Image_table where iamge_id = ?;

5.根据MD5查看图片

public Image selectByMd5(String md5)
用到的数据库语句:select * from Image_table where md5= ?

数据库操作中插入删除数据用executeUpdate()方法;
查询使用executeQuery()方法

实现Servlet

首先要安装servlet,maven下载依赖。
创建一个类,继承HttpServlet父类,并且覆写父类中的一些方法
具体覆写什么方法要看服务器收到的是什么请求。

1、ImageServlet(主要以上传dopost为例)**

1.doGet()
实现产看指定图片和查看全部图片
2.doPost

@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取图片属性信息,并存入数据库
            //创建factory对象还让upload对象
        FileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
            //通过upload对象进一步解析请求
            //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,\"reson\":\"请求解析失败\"}");
            return;
        }
            //将FileItem中的属性提取出来,转换成Image对象,存入数据库中
        FileItem fileItem = items.get(0);
        Image image = new Image();
        image.setImage_name(fileItem.getName());
        image.setSize((int)fileItem.getSize());
            //手动获取时间,并转成格式化日期
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        image.setUploadTime(simpleDateFormat.format(new Date()));
        image.setContentType(fileItem.getContentType());
       // image.setMd5("112233445566");
        //计算MD5
        image.setMd5(DigestUtils.md5Hex(fileItem.get()));
         /*   //构造路径进行保存,加入时间戳,让文件路径唯一
        image.setPath("./image/"+System.currentTimeMillis()+fileItem.getName());
        */
         //改进2,利用MD5来表示磁盘文件名,相同内容的只存一份,优化磁盘
        image.setPath("./image/"+image.getMd5());
            //存入数据库中
        ImageDao imageDao = new ImageDao();
        Image image_mad5 = imageDao.selectByMd5(image.getMd5());
        imageDao.insert(image);
        //2.获取图片内容信息,并写入磁盘文件
        if(image_mad5 == 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,\"reson\":\"写磁盘失败\"}");
                return;
            }
        }
        //3.给客户端返回一个结果数据
//        resp.setContentType("aplication/json; charset=utf-8");
//        resp.getWriter().write("{\"ok\":true}");

        //上传之后可以直接看到新上传的图片
        resp.sendRedirect("images.html");
    }
实现项目过程中出现乱码问题:
汉字是按照一定方式格式进行编码的,编码格式典型的有两种:
1.GBK(浏览器默认按照操作系统默认编码解析,而对于windowsj简体中文默认GBK)
2.	UTF8(IDEA创建文件默认)
这样就会出现问题,代码中返回UTF8格式,而浏览器按照GBK理解,这样就会出现错误,需要告诉浏览器按照UTF8解析。
代码中体现就是:resp.setContentType("application/json; charset=utf-8");

3.doDelete()
实现删除业务

2、ImageShowServlet:对应查看图片内容功能
//需要定义一个set进行存储允许的referer实现防盗连机制
@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,\"reson\":\"未授权的访问\"}");
            return;
        }
        //1.解析出image_id
        String image_id = req.getParameter("image_id");
        if(image_id == null || image_id.equals("")){
            resp.setStatus(200);
            resp.setContentType("application/json; charset=utf-8");
            resp.getWriter().write("{\"ok\":false,\"reson\":\"请求解析失败\"}");
            return;
        }
        //2.在数据库中查找,并且得到图片存储的路径,
        ImageDao imageDao = new ImageDao();
        Image image = imageDao.selectOne(Integer.parseInt(image_id));
        if(image == null){
            //数据库中不存在要删除的image_id
            resp.setStatus(200);
            resp.setContentType("application/json; charset=utf-8");
            resp.getWriter().write("{\"ok\":false,\"reson\":\"数据库中不存在对应的image_id\"}");
            return;
        }
        //3.根据路径打开文件,进行读取并写入响应中
        resp.setContentType(image.getContentType());
        File file = new File(image.getPath());
        //图片是二进制文件,用字节流的方式进行读取文件
        FileInputStream fileInputStream = new FileInputStream(file);
        OutputStream outputStream = resp.getOutputStream();
        //buffer缓冲区
        byte[] buffer = new byte[1024];
        while(true){
            //将内容读到buffer中
            int len = fileInputStream.read(buffer);
            if(len == -1){
                //文件读取结束
                break;
            }
            //将读到的部分写到响应对象中
            outputStream.write(buffer);
        }
        fileInputStream.close();
        outputStream.close();
    }

前端页面

使用HTML+CSS+JavaScript实现一个前端页面,并将项目部署到云服务器上。
HTML:网页的骨架
CSS:描述网页上组件的样式(位置,颜色,字体,大小,背景…)
JavaScript:逻辑实现

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Java微服务项目中使用Vue上传图片到Linux服务器,您可以按照以下步骤进行操作: 1. 在Vue项目中,使用`vue-axios`或`axios`库来发送HTTP请求。确保您已经安装了这些库。 2. 在Vue组件中创建一个文件上传的表单,并添加一个文件选择器: ```html <template> <div> <input type="file" @change="uploadImage" /> </div> </template> ``` 3. 在Vue组件的方法中,使用`FormData`对象来创建一个表单数据,并将选择的文件附加到其中: ```javascript methods: { uploadImage(event) { const file = event.target.files[0]; const formData = new FormData(); formData.append('image', file); // 发送HTTP请求 axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) .then(response => { // 处理成功响应 }) .catch(error => { // 处理错误响应 }); } } ``` 在这个例子中,我们使用了`axios.post`方法来发送POST请求,将文件数据作为表单数据发送到服务器的`/api/upload`接口。 4. 在Java微服务项目中,编写一个接口来处理文件上传的请求。您可以使用Spring Boot的MultipartFile来处理文件上传: ```java @PostMapping("/api/upload") public ResponseEntity<String> uploadImage(@RequestParam("image") MultipartFile image) { // 获取上传的文件名 String fileName = image.getOriginalFilename(); // 指定文件保存路径 String savePath = "/path/to/save/images/" + fileName; try { // 将文件保存到服务器 Path path = Paths.get(savePath); Files.write(path, image.getBytes()); return ResponseEntity.ok("文件上传成功"); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败"); } } ``` 在这个例子中,我们使用了`@RequestParam`注解来接收上传的文件,并使用`Files.write`方法将文件保存到服务器指定的路径。 请注意,您需要根据实际情况修改保存路径和其他相关代码。 这样,当用户在Vue前端选择文件并触发上传动作时,文件将被发送到Java后端并保存到Linux服务器上。希望这个回答对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值