分布式文件系统 SpringBoot+FastDFS+Vue.js

分布式文件系统 SpringBoot+FastDFS+Vue.js

  • 一、分布式文件系统
    • 1.1.文件系统
    • 1.2.什么是分布式文件系统
    • 1.3.分布式文件系统的出现
    • 1.3.主流的分布式文件系统
    • 1.4.分布式文件服务提供商
      • 1.4.1.阿里OSS
      • 1.4.2.七牛云存储
      • 1.4.3.百度云存储
  • 二、fastDFS
    • 2.1.fastDSF介绍
    • 2.2.为什么要使用fastDFS
    • 2.3.fastDSF工作原理
      • 2.3.1.fastDSF架构
      • 2.3.2.文件上传流程
      • 2.3.3.文件下载流程
  • 三、fastDFS入门
    • 3.1.fastDFS安装与配置
    • 3.2.文件上传下载测试
      • 3.2.1.搭建环境
        • 3.2.1.1.创建maven工程
        • 3.2.1.2.添加依赖
        • 3.2.1.3.配置文件
      • 3.2.2.文件上传
        • 3.2.2.1.文件上传代码实现
        • 3.2.2.2.运行结果
        • 3.2.2.3.测试上传结果-访问
      • 3.2.3.文件查询
        • 3.2.3.1.文件查询代码实现
        • 3.2.3.2.运行结果
      • 3.2.4.文件下载
        • 3.2.4.1.文件下载代码实现
        • 3.2.4.2.运行结果
      • 3.2.5.文件删除
        • 3.2.5.1.文件删除代码实现
        • 3.2.5.2.运行结果
        • 3.2.5.3.服务器查看
    • 3.3.recv body length: 70 is not correct, expect length: 40
      • 3.3.1.解决问题:修改依赖
      • 3.3.2.下载源码打包成jar包并上传到本地maven仓库
        • 3.3.2.1.下载解压
        • 3.3.2.2.使用maven从源码安装
        • 3.3.2.3.进入/target 目录,执行CMD 命令,将jar包打包到本地maven仓库
        • 3.3.2.4.在maven项目pom.xml中添加fastdfs-client-java依赖
  • 四、文件服务案例
    • 4.1.目标:使用fastDSF实现图片服务器
    • 4.2.需求分析
    • 4.3.功能开发
      • 4.3.1.搭建fastDFS文件服务器
      • 4.3.2.搭建文件管理服务
        • 4.3.2.1.添加依赖
        • 4.3.2.2.fastdfs-client配置文件
        • 4.3.2.3.跨域配置CrossConfig.java
        • 4.3.2.4.创建模型--实体类
          • 4.3.2.4.1.FileSystem.java
          • 4.3.2.4.2.Result.java
        • 4.3.2.5.创建controller
        • 4.3.2.6.创建优化后的controller代码
        • 4.3.2.7.创建application.yml
        • 4.3.2.8.创建spring boot启动类
      • 4.3.3.管理系统前端【Vue2】项目
        • 4.3.3.1.axios模块
          • 4.3.3.1.1.安装axios模块
          • 4.3.3.1.2.在入口文件main.js中配置axios
          • 4.3.3.1.3.axios使用案例
          • 4.3.3.1.4.vue-axios使用案例
        • 4.3.3.2.关闭Vue的生产提示
        • 4.3.3.3.vue项目中无router文件夹,vue安装路由
        • 4.3.3.4.引入Vue Router路由Vue.js【v.3x】
        • 4.3.3.5.引入ElementUI组件库【Vue2 版本】
        • 4.3.3.6.main.js完整版
        • 4.3.3.7.页面创建UploadImg.vue
        • 4.3.3.8.修改App.vue
        • 4.3.3.9.创建路由配置router/index.js
        • 4.3.3.10.课程图片浏览
      • 4.3.4.管理系统前端【Vue3】项目
        • 4.3.4.1.axios模块
          • 4.3.4.1.1.安装axios模块
          • 4.3.4.1.2.在入口文件main.js中配置axios
        • 4.3.4.2.关闭Vue的生产提示
        • 4.3.4.3.vue项目中无router文件夹,vue安装路由
        • 4.3.4.4.引入Vue Router路由Vue.js【v.4x】
        • 4.3.4.5.引入Element Plus组件库【Vue3 版本】
        • 4.3.4.6.main.js完整版
        • 4.3.4.7.页面创建UploadImg.vue
        • 4.3.4.8.修改App.vue
        • 4.3.4.9.创建路由配置router/index.js
        • 4.3.4.10.页面效果
  • 五、总结
  • endl

一、分布式文件系统

1.1.文件系统

操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。

常见的文件系统:FAT16/FAT32、NTFS、HFS、UFS、APFS、XFS、Ext4等 。

1.2.什么是分布式文件系统

分布式文件系统(Distributed File System,DFS)是指文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点(可简单的理解为一台计算机)相连;或是若干不同的逻辑磁盘分区或卷标组合在一起而形成的完整的有层次的文件系统。

DFS为分布在网络上任意位置的资源提供一个逻辑上的树形文件系统结构,从而使用户访问分布在网络上的共享文件更加简便。
单独的 DFS共享文件夹的作用是相对于通过网络上的其他共享文件夹的访问点。

分布式文件系统中的数据存储在多台机器上,这些专门用来存储数据的机器称之为存储节点,由多个节点构成分布式集群,节点上的小的分布式文件系统组合成总的分布式文件系统,由主服务器对总的文件系统进行管理。用户任意访问某一台主机,都能获取到自己想要的目标文件。

1.3.分布式文件系统的出现

分布式文件系统是面对互联网的需求而产生,互联网时代对海量数据如何存储?靠简单的增加硬盘的个数已经满足不了我们的要求,因为硬盘传输速度有限但是数据在急剧增长,另外我们还要做好数据备份、数据安全等。

采用分布式文件系统可以将多个地点的文件系统通过网络连接起来,组成一个文件系统网络,结点之间通过网络进行通信,一台文件系统的存储和传输能力有限,我们让文件在多台计算机上存储,通过多台计算共同传输。

1.3.主流的分布式文件系统

  • NFS(网络文件系统)
  • GFS(googleFS)
  • HDFS(hadoop分布式文件系统)
  • FastDFS

1.4.分布式文件服务提供商

1.4.1.阿里OSS

对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

1.4.2.七牛云存储

1.4.3.百度云存储

二、fastDFS

2.1.fastDSF介绍

FastDFS是用c语言编写的一款开源的分布式文件系统,它是由淘宝资深架构师余庆编写并开源。
FastDFS专为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

FastDFS 系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。

  • Tracker Server:跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的 storage server和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。
  • Storage Server:存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。
  • Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。

2.2.为什么要使用fastDFS

NFS、GFS都是通用的分布式文件系统,通用的分布式文件系统优点的是开发体验好,但是系统复杂性高、性能一般,而专用的分布式文件系统虽然开发体验性差,但是系统复杂性低并且性能高
fastDFS非常适合存储图片等那些小文件,fastDFS不对文件进行分块,所以它就没有分块合并的开销,fastDFS网络通信采用socket,通信速度很快。

FastDFS特点:

  • 分组存储,简单灵活;
  • 对等结构,不存在单点;
  • 文件ID由FastDFS生成,作为文件访问凭证。FastDFS不需要传统的name server或meta server;
  • 大、中、小文件均可以很好支持,可以存储海量小文件;
  • 一台storage支持多块磁盘,支持单盘数据恢复;
  • 提供了nginx扩展模块,可以和nginx无缝衔接;
  • 支持多线程方式上传和下载文件,支持断点续传;
  • 存储服务器上可以保存文件附加属性。

2.3.fastDSF工作原理

2.3.1.fastDSF架构

FastDFS架构包括 Tracker server和Storageserver。

客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。
在这里插入图片描述
1)Tracker

Tracker Server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server提供文件上传服务。
可以将tracker称为追踪服务器或调度服务器。

FastDFS集群中的Tracker server可以有多台,Tracker server之间是相互平等关系同时提供服务,Tracker server不存在单点故障。
客户端请求Tracker server采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。

2)Storage
Storage Server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己的文件系统而是使用操作系统的文件系统来管理文件。可以将storage称为存储服务器

Storage集群采用了分组存储方式。storage集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容量之和。一个组由一台或多台存储服务器组成,组内的Storage server之间是平等关系,不同组的Storage server之间不会相互通信,同组内的Storage server之间会相互连接进行文件同步,从而保证同组内每个storage上的文件完全一致的。一个组的存储容量为该组内存储服务器容量最小的那个,由此可见组内存储服务器的软硬件配置最好是一致的。

采用分组存储方式的好处是灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组也可以由tracker进行调度选择。一个分组的存储服务器访问压力较大时,可以在该组增加存储服务器来扩充服务能力(纵向扩容)。当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。

3)Storage状态收集

Storage server会连接集群中所有的Tracker server,定时向他们报告自己的状态,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。

2.3.2.文件上传流程

在这里插入图片描述

  • storage定时向tracker上传状态信息
  • Client向tracker上传连接请求
  • tracker查询可用的storage
  • tracker向client返回信息(storage的ip和端口)
  • client向storage上传文件(file content和metadata)
  • storage生成一个file_id
  • storage将上传内容写入磁盘
  • storage向client返回file_id(文件名和文件存储的路径信息)
  • client完成文件信息的存储

客户端上传文件后存储服务器文件ID返回给客户端,此文件ID用于以后访问该文件的索引信息。
文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

group1 /M00 /02/44/ wKgDrE34E8wAAAAAAAAGkEIYJK42378.sh

  • 组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存。
  • 虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推。
  • 数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
  • 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

2.3.3.文件下载流程

在这里插入图片描述

  • storage定时向tracker上传状态信息
  • client向tracker提交下载连接请求
  • tracker查询可用的storage
  • tracker向client返回信息(storage的ip和端口)
  • client向storage提交信息file_id(组名、路径、文件名)
  • storage查看文件
  • storage返回file_content给client

tracker根据请求的文件路径即文件ID 来快速定义文件。比如请求下边的文件:

在这里插入图片描述

  1. 通过组名tracker能够很快的定位到客户端需要访问的存储服务器组是group1,并选择合适的存储服务器提供客户端访问。
  2. 存储服务器根据“文件存储虚拟磁盘路径”和“数据文件两级目录”可以很快定位到文件所在目录,并根据文件名找到客户端需要访问的文件。

三、fastDFS入门

3.1.fastDFS安装与配置

详见:https://blog.csdn.net/qq_45740503/article/details/136086731

3.2.文件上传下载测试

3.2.1.搭建环境

参考官方文档java版本的fastdfs-client地址在:https://github.com/happyfish100/fastdfs-client-java,参考此工程编写测试用例。
在这里插入图片描述

3.2.1.1.创建maven工程
3.2.1.2.添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>

    <groupId>com.orange</groupId>
    <artifactId>fastDFSLearn01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--fastdfs-client-java依赖需要自己手动打包上传到本地仓库-->
        <dependency>
            <groupId>org.csource</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.31-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

</project>
3.2.1.3.配置文件

在classpath:config下创建fastdfs-client.properties文件

## fastdfs-client.properties

#fastDFS连接超时时间,针对socket套接字函数connect
connect_timeout_in_seconds = 5
#fastDFS网络超时时间
network_timeout_in_seconds = 30

#编码格式
charset = UTF-8

#是否启用token验证(针对fdfs配置文件/etc/fdfs/http.conf,防盗链)
http_anti_steal_token = false
#连接密钥(http.conf要配置一样的密钥)
http_secret_key = FastDFS1234567890
#tracker服务器访问端口
http_tracker_http_port = 80

#tracker服务器地址,多个以逗号隔开
fastdfs.tracker_servers = 192.168.229.141:22122

3.2.2.文件上传

3.2.2.1.文件上传代码实现
/**
 * 在此文件中通过fastDSF的client代码访问tracker和storage
 * 通过client的api代码方便 访问 tracker和storage,它们中间走的socket协议
 */
public class TestFastDFSUpload {
    //测试文件上传
    @Test
    public void Upload() {
        //通过fastDSF的client代码访问tracker和storage
        try {
            //加载fastDFS客户端的配置 文件
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            System.out.println("charset=" + ClientGlobal.g_charset);

            //创建tracker的客户端
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.getG_tracker_group());
            //通过TrackerClient对象获取TrackerServer信息
            //1.29版本以前的方法
            //TrackerServer trackerServer = trackerClient.getConnection();
            //没有trackerClient.getConnection()方法的问题解决
            //1.29版本以后的fastdfs的方法更新
            TrackerServer trackerServer = trackerClient.getTrackerServer();
            StorageServer storageServer = null;

            //每次调用的时候会重新new一个StorageClient()实例,这样每次请求拿到的就是不同的StorageClient,
            //也就意味着每个请求会获取到不同的storageServer,这样就不存在共享变量,也就避免了出现并发的空指针问题
            //最好的解决方案就是每次调用的时候new一个新的实例去使用。在使用FastDFS的时候,尽量不要重用StorageClient
            //参考文章:https://blog.csdn.net/luckykapok918/article/details/80938257
            //定义storage的客户端,建立与Storage服务器的连接
            StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);

            //文件元信息
            NameValuePair[] metaList = new NameValuePair[1];
            metaList[0] = new NameValuePair("fileName", "52.png");
            String path = "D:\\image\\52.png";

            //执行上传
            String fileInfoes = storageClient.upload_file1(path, "png", metaList);
            System.out.println("upload success. file id is: " + fileInfoes);

            //关闭storage客户端
            storageClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
3.2.2.2.运行结果
network_timeout=30000ms
charset=UTF-8
upload success. file id is: group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png

Process finished with exit code 0
3.2.2.3.测试上传结果-访问
cd /home/fastdfs/fdfs_storage/data/00/00
http://192.168.229.141/group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2.3.文件查询

3.2.3.1.文件查询代码实现
public class TestFastDFSQuery {
    //测试文件查询
    @Test
    public void Query() {
        //通过fastDSF的client代码访问tracker和storage
        try {
            //加载fastDFS客户端的配置 文件
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            System.out.println("charset=" + ClientGlobal.g_charset);

            //创建tracker的客户端
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.getG_tracker_group());
            //通过TrackerClient对象获取TrackerServer信息
            TrackerServer trackerServer = trackerClient.getTrackerServer();
            StorageServer storageServer = null;

            //定义storage的客户端,建立与Storage服务器的连接
            StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);

            //查询文件
            //upload success. file id is: group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png
            String group_name = "group1";
            String remoteFileName = "M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png";
            String file_id = "group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png";
            FileInfo fileInfo = storageClient.query_file_info(group_name, remoteFileName);
            System.out.println(fileInfo);
            FileInfo fileInfo1 = storageClient.query_file_info1(file_id);
            System.out.println(fileInfo1);

            //查询文件元信息
            NameValuePair[] metadata = storageClient.get_metadata1(file_id);
            for (NameValuePair pair : metadata) {
                System.out.println("文件元信息 : " + pair.getName() + " ," + pair.getValue());
            }

            //关闭storage客户端
            storageClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
3.2.3.2.运行结果
network_timeout=30000ms
charset=UTF-8
fetch_from_server = true, file_type = 1, source_ip_addr = 192.168.229.141, file_size = 372928, create_timestamp = 2024-02-10 12:55:07, crc32 = -743583210
fetch_from_server = true, file_type = 1, source_ip_addr = 192.168.229.141, file_size = 372928, create_timestamp = 2024-02-10 12:55:07, crc32 = -743583210
文件元信息 : fileName ,52.png

Process finished with exit code 0

3.2.4.文件下载

3.2.4.1.文件下载代码实现
public class TestFastDFSDownload {
    //测试文件下载
    @Test
    public void Download() {
        //通过fastDSF的client代码访问tracker和storage
        try {
            //加载fastDFS客户端的配置 文件
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            System.out.println("charset=" + ClientGlobal.g_charset);

            //创建tracker的客户端
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.getG_tracker_group());
            //通过TrackerClient对象获取TrackerServer信息
            TrackerServer trackerServer = trackerClient.getTrackerServer();
            StorageServer storageServer = null;

            //定义storage的客户端,建立与Storage服务器的连接
            StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);

            //查询文件
            //upload success. file id is: group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png
            String group_name = "group1";
            String remoteFileName = "M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png";
            String file_id = "group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png";
            FileInfo fileInfo = storageClient.query_file_info(group_name, remoteFileName);
            System.out.println("fileInfo = " + fileInfo);

            if (fileInfo == null) {
                System.out.println("您下载的文件信息不存在,请核对后再次下载......");
                return;
            }

            byte[] bytes = storageClient.download_file1(file_id);
            File file = new File("D:\\image\\a.png");
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(bytes);
            fos.close();

            //关闭storage客户端
            storageClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
3.2.4.2.运行结果
network_timeout=30000ms
charset=UTF-8
fileInfo = fetch_from_server = true, file_type = 1, source_ip_addr = 192.168.229.141, file_size = 372928, create_timestamp = 2024-02-10 12:55:07, crc32 = -743583210

Process finished with exit code 0

3.2.5.文件删除

3.2.5.1.文件删除代码实现
public class TestFastDFSDelete {
    //测试文件删除
    @Test
    public void Delete() {
        try {
            //加载fastDFS客户端的配置 文件
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            System.out.println("charset=" + ClientGlobal.g_charset);

            //创建tracker的客户端
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.getG_tracker_group());
            //通过TrackerClient对象获取TrackerServer信息
            TrackerServer trackerServer = trackerClient.getTrackerServer();
            StorageServer storageServer = null;

            //定义storage的客户端,建立与Storage服务器的连接
            StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);

            //查询文件
            //upload success. file id is: group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png
            String group_name = "group1";
            String remoteFileName = "M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png";
            String file_id = "group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png";
            FileInfo fileInfo = storageClient.query_file_info(group_name, remoteFileName);
            System.out.println("fileInfo = " + fileInfo);

            if (fileInfo == null) {
                System.out.println("您删除的文件信息不存在,请核对后再次删除......");
                return;
            }

            storageClient.delete_file1(file_id);
            System.out.println("删除成功");

            //关闭storage客户端
            storageClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
3.2.5.2.运行结果
network_timeout=30000ms
charset=UTF-8
fileInfo = fetch_from_server = true, file_type = 1, source_ip_addr = 192.168.229.141, file_size = 372928, create_timestamp = 2024-02-10 12:55:07, crc32 = -743583210
删除成功

Process finished with exit code 0
3.2.5.3.服务器查看

在这里插入图片描述

3.3.recv body length: 70 is not correct, expect length: 40

network_timeout=30000ms
charset=UTF-8
java.io.IOException: recv body length: 70 is not correct, expect length: 40
	at org.csource.fastdfs.ProtoCommon.recvHeader(ProtoCommon.java:186)
	at org.csource.fastdfs.ProtoCommon.recvPackage(ProtoCommon.java:201)
	at org.csource.fastdfs.TrackerClient.getStoreStorage(TrackerClient.java:130)
	at org.csource.fastdfs.StorageClient.newWritableStorageConnection(StorageClient.java:1627)
	at org.csource.fastdfs.StorageClient.do_upload_file(StorageClient.java:639)
	at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:120)
	at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:91)
	at org.csource.fastdfs.StorageClient.upload_file(StorageClient.java:73)
	at org.csource.fastdfs.StorageClient1.upload_file1(StorageClient1.java:64)

server 端使用 V6.11,fastdfs-clint-java需要使用最新的 V1.31

3.3.1.解决问题:修改依赖

<dependency>
    <groupId>org.csource</groupId>
    <artifactId>fastdfs-client-java</artifactId>
    <version>1.31-SNAPSHOT</version>
</dependency>

3.3.2.下载源码打包成jar包并上传到本地maven仓库

FastDFS java下载官网:https://github.com/happyfish100/fastdfs-client-java

源码及本人打包好的jar包:https://www.lanzv.com/b05ew922f 密码:deca
在这里插入图片描述
在这里插入图片描述

3.3.2.1.下载解压

在这里插入图片描述
在这里插入图片描述

3.3.2.2.使用maven从源码安装
mvn -version

mvn clean install

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3.2.3.进入/target 目录,执行CMD 命令,将jar包打包到本地maven仓库
mvn install:install-file -DgroupId=org.csource -DartifactId=fastdfs-client-java -Dversion=${version} -Dpackaging=jar -Dfile=fastdfs-client-java-${version}.jar

执行以下命令:【具体版本,路径按实际修改】

mvn install:install-file -DgroupId="org.csource" -DartifactId=fastdfs-client-java -Dversion="1.31-SNAPSHOT" -Dpackaging=jar -Dfile="D:\FastDFS-java\fastdfs-client-java-1.31\target\fastdfs-client-java-1.31-SNAPSHOT.jar"

在这里插入图片描述
在这里插入图片描述

3.3.2.4.在maven项目pom.xml中添加fastdfs-client-java依赖
<dependency>
    <groupId>org.csource</groupId>
    <artifactId>fastdfs-client-java</artifactId>
    <version>1.31-SNAPSHOT</version>
</dependency>

在这里插入图片描述

四、文件服务案例

4.1.目标:使用fastDSF实现图片服务器

4.2.需求分析

在这里插入图片描述

4.3.功能开发

4.3.1.搭建fastDFS文件服务器

在这里插入图片描述

4.3.2.搭建文件管理服务

4.3.2.1.添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>

    <groupId>com.orange</groupId>
    <artifactId>fastDFSLearn01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--fastdfs-client-java依赖需要自己手动打包上传到本地仓库-->
        <dependency>
            <groupId>org.csource</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.31-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

</project>
4.3.2.2.fastdfs-client配置文件

fastdfs-client.properties

## fastdfs-client.properties

#fastDFS连接超时时间,针对socket套接字函数connect
connect_timeout_in_seconds = 5
#fastDFS网络超时时间
network_timeout_in_seconds = 30

#编码格式
charset = UTF-8

#是否启用token验证(针对fdfs配置文件/etc/fdfs/http.conf,防盗链)
http_anti_steal_token = false
#连接密钥(http.conf要配置一样的密钥)
http_secret_key = FastDFS1234567890
#tracker服务器访问端口
http_tracker_http_port = 80

#tracker服务器地址,多个以逗号隔开
fastdfs.tracker_servers = 192.168.229.141:22122
4.3.2.3.跨域配置CrossConfig.java
@Configuration
public class CrossConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")// 对所有路径应用跨域配置,所有的当前站点的请求地址,都支持跨域访问。
                //是否发送Cookie
                .allowCredentials(true)
                //放行哪些原始域
                .allowedHeaders("*")
                .allowedMethods("POST", "GET", "HEAD", "PUT", "OPTIONS", "DELETE")
                .allowedOriginPatterns("*")// 所有的外部域都可跨域访问。
                // 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
                .maxAge(3600);// 超时时长设置为1小时。 时间单位是秒。
    }
}
4.3.2.4.创建模型–实体类
4.3.2.4.1.FileSystem.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FileSystem {
    private String fileId;
    private String filePath;
    private Long fileSize;
    private String fileName;
    private String fileType;
}
4.3.2.4.2.Result.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;//响应码,1 代表成功; 0 代表失败
    private String msg;  //响应信息 描述字符串
    private Object data; //返回的数据

    //增删改 成功响应
    public static Result success(){
        return new Result(1,"success",null);
    }
    //查询 成功响应
    public static Result success(Object data){
        return new Result(1,"success",data);
    }
    //失败响应
    public static Result error(String msg){
        return new Result(0,msg,null);
    }
}
4.3.2.5.创建controller
@Slf4j
@RestController
@RequestMapping("/filesystem")
public class FileServerController {

    @Value("${orange-fastdfs.upload_location}")
    private String upload_location;

    @PostMapping("/uploadFile")
    @ResponseBody
    public Result upload(@RequestParam("file") MultipartFile file) throws IOException {
        //将文件先存储在web服务器上(本机),在调用fastDFS的client将文件上传到 fastDFS服务器
        FileSystem fileSystem = new FileSystem();

        //文件原始名称
        String originalFilename = file.getOriginalFilename();

        //文件扩展名比如22.jpg
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        log.info("文件扩展名后缀 = {}", extension);//.jpg
        String filenameExtension = StringUtils.getFilenameExtension(originalFilename);
        log.info("文件类型 = {}", filenameExtension);//jpg
        if (filenameExtension == null) {
            return Result.error("此文件没有文件扩展名");
        }

        //新文件名称
        String fileName = UUID.randomUUID().toString().replace("-", "") + "." + filenameExtension;
        log.info("新文件名称 = {}", fileName);

        //定义file,使用file存储上传的文件
        //File file1 = new File("D:\\image\\upload\\" + fileName);
        File file1 = new File(upload_location + fileName);
        //指定照片上传路径,上传的文件写入到新的文件
        file.transferTo(file1);
        //获取新上传文件的物理路径
        String newFilePath = file1.getAbsolutePath();

        //通过fastDSF的client代码访问tracker和storage
        try {
            //加载fastDFS客户端的配置 文件
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            System.out.println("charset=" + ClientGlobal.g_charset);

            //创建tracker的客户端
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.getG_tracker_group());
            //通过TrackerClient对象获取TrackerServer信息
            TrackerServer trackerServer = trackerClient.getTrackerServer();
            StorageServer storageServer = null;

            //定义storage的客户端,建立与Storage服务器的连接
            StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);

            //文件元信息
            NameValuePair[] metaList = new NameValuePair[1];
            metaList[0] = new NameValuePair("fileName", "52.png");

            //执行上传
            String fileId = storageClient.upload_file1(newFilePath, filenameExtension, metaList);
            System.out.println("upload success. file id is: " + fileId);
            fileSystem.setFileId(fileId);
            fileSystem.setFilePath(fileId);
            fileSystem.setFileName(originalFilename);
            fileSystem.setFileSize(file.getSize());
            fileSystem.setFileType(filenameExtension);
            //通过调用service及dao将文件的路径存储到数据库中

            //关闭storage客户端
            storageClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return Result.success(fileSystem);
    }
}
4.3.2.6.创建优化后的controller代码
@Slf4j
@RestController
@RequestMapping("/filesystem")
public class FileServerController {

    @PostMapping("/uploadFile")
    @ResponseBody
    public Result upload(@RequestParam("file") MultipartFile file) throws IOException {
        //将文件先存储在web服务器上(本机),在调用fastDFS的client将文件上传到 fastDFS服务器
        FileSystem fileSystem = new FileSystem();

        String contentType = file.getContentType();
        log.info("上传的文件类型为:{}", contentType);

        byte[] file_buff = null;
        //把文件转成输入流
        InputStream inputStream = file.getInputStream();
        if (inputStream != null) {
            //获取输入流中可读取的数据大小
            int len = inputStream.available();
            //创建足够大的缓冲区
            file_buff = new byte[len];
            //一次性把输入流中的数据全都读入到缓冲区file_buff,那file_buff就要足够大,占用内存也会很大
            inputStream.read(file_buff);
        }
        //关闭输入流
        inputStream.close();

        //文件原始名称
        String originalFilename = file.getOriginalFilename();

        //文件扩展名比如22.jpg
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        log.info("文件扩展名后缀 = {}", extension);//.jpg
        String filenameExtension = StringUtils.getFilenameExtension(originalFilename);
        log.info("文件类型 = {}", filenameExtension);//jpg
        if (filenameExtension == null) {
            return Result.error("此文件没有文件扩展名");
        }

        //通过fastDSF的client代码访问tracker和storage
        try {
            //加载fastDFS客户端的配置 文件
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            System.out.println("charset=" + ClientGlobal.g_charset);

            //创建tracker的客户端
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.getG_tracker_group());
            //通过TrackerClient对象获取TrackerServer信息
            TrackerServer trackerServer = trackerClient.getTrackerServer();
            StorageServer storageServer = null;

            //定义storage的客户端,建立与Storage服务器的连接
            StorageClient1 storageClient = new StorageClient1(trackerServer, storageServer);

            //文件元信息
            NameValuePair[] metaList = new NameValuePair[1];
            metaList[0] = new NameValuePair("fileName", "52.png");

            //执行上传
            String fileId = storageClient.upload_file1(file_buff, filenameExtension, metaList);
            System.out.println("upload success. file id is: " + fileId);
            fileSystem.setFileId(fileId);
            fileSystem.setFilePath(fileId);
            fileSystem.setFileName(originalFilename);
            fileSystem.setFileSize(file.getSize());
            fileSystem.setFileType(contentType);
            //通过调用service及dao将文件的路径存储到数据库中

            //关闭storage客户端
            storageClient.close();
        } catch (Exception e) {
            log.error("上传文件失败:", e);
            e.printStackTrace();
        }

        return Result.success(fileSystem);
    }
}
4.3.2.7.创建application.yml
server:
  port: 22100

orange-fastdfs:
  #文件上传临时目录
  upload_location: D:\\image\\upload\\

# linux临时文件目录
# mkdir -p /data/tmp/updatefile
spring:
  servlet:
    multipart:
      location: /data/tmp/updatefile
4.3.2.8.创建spring boot启动类
@SpringBootApplication
public class FileServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileServerApplication.class, args);
    }
}

4.3.3.管理系统前端【Vue2】项目

4.3.3.1.axios模块
4.3.3.1.1.安装axios模块

vue-axios|axios中文网:http://www.axios-js.com/zh-cn/docs/vue-axios.html
axios官网:https://www.axios-http.cn/docs/intro

npm install --save axios vue-axios

//报错时使用--legacy-peer-deps
npm install --save axios vue-axios --legacy-peer-deps
4.3.3.1.2.在入口文件main.js中配置axios
#在入口文件main.js中配置
//引入vue
import Vue from 'vue'
//引入axios
import axios from 'axios'
//引入VueAxios
//安装VueAxios模块后,不需要在每个组件中单独导入axios,只需将axios请求改为this.axios
import VueAxios from 'vue-axios'
//把axios挂载到vue上
Vue.prototype.$axios = axios;

//使用Vue.use来注册安装插件
Vue.use(VueAxios, axios)

new Vue({
  el:'#app',
  render: h => h(App),
})
按照这个顺序分别引入这三个文件: vue, axios and vue-axios
4.3.3.1.3.axios使用案例
# 在入口文件main.js中配置
//引入Vue
import Vue from 'vue'
//引入axios
import axios from 'axios'
//把axios挂载到vue上
Vue.prototype.$axios = axios

new Vue({
  el:'#app',
  render: h => h(App),
})
# 第三步:使用案例
this.$axios.get('/user?id=888').then((response) => {
  console.log(response.data)
}).catch( (error) => {
    console.log(error);
});
4.3.3.1.4.vue-axios使用案例
#在入口文件main.js中配置
//引入Vue
import Vue from 'vue'
//引入axios
import axios from 'axios'
//引入VueAxios
import VueAxios from 'vue-axios'

//使用Vue.use来注册安装插件
Vue.use(VueAxios, axios)

new Vue({
  el:'#app',
  render: h => h(App),
})
#第三步:使用方式有如下三种
#方式1
Vue.axios.get(api).then((response) => {
  console.log(response.data)
})
#方式2
this.axios.get(api).then((response) => {
  console.log(response.data)
})
#方式3
this.$http.get(api).then((response) => {
  console.log(response.data)
})
4.3.3.2.关闭Vue的生产提示
#在入口文件main.js中配置
//关闭Vue的生产提示
Vue.config.productionTip = false
4.3.3.3.vue项目中无router文件夹,vue安装路由

新建一个router文件夹,在文件夹下新建一个index.js文件

在这里插入图片描述

4.3.3.4.引入Vue Router路由Vue.js【v.3x】

Vue2使用v.3x
Vue Router官网【v3.x】:https://v3.router.vuejs.org/zh/
更新记录【3.x】https://github.com/vuejs/vue-router/releases

//报错时使用--legacy-peer-deps
//vue-router@3x 适用于 vue2
npm install vue-router@3

//指定版本号
npm install vue-router@3.5.2 --save

在这里插入图片描述

#在入口文件main.js中配置
//引入VueRouter
import VueRouter from 'vue-router'

//使用Vue.use来注册安装插件
Vue.use(VueRouter)

//新建一个router文件夹,在文件夹下新建一个index.js文件
//引入路由器
import router from './router/index'

// 创建和挂载根实例
new Vue({
  router, //将路由器注入到new Vue实例中,建立关联
  render: h => h(App),
}).$mount('#app');
4.3.3.5.引入ElementUI组件库【Vue2 版本】

ElementUI组件库官网:https://element.eleme.cn/#/zh-CN

npm i element-ui -S
//完整引入
//引入ElementUI组件库
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

//使用ElementUI组件库
Vue.use(ElementUI)
4.3.3.6.main.js完整版

在这里插入图片描述

import App from './App.vue'
//引入Vue
import Vue from 'vue'
//引入axios
import axios from 'axios'
//引入VueAxios
//安装VueAxios模块后,不需要在每个组件中单独导入axios,只需将axios请求改为this.axios
import VueAxios from 'vue-axios'
//引入VueRouter
import VueRouter from 'vue-router'
//完整引入
//引入ElementUI组件库
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
//新建一个router文件夹,在文件夹下新建一个index.js文件
//引入路由器
import router from './router/index'

//把axios挂载到vue上
Vue.prototype.$axios = axios;
//使用Vue.use来注册安装插件
Vue.use(VueRouter)
Vue.use(router)
Vue.use(VueAxios, axios)
//使用ElementUI组件库
Vue.use(ElementUI)
//关闭Vue的生产提示
Vue.config.productionTip = false

// 创建和挂载根实例
new Vue({
  router, //将路由器注入到new Vue实例中,建立关联
  render: h => h(App),
}).$mount('#app');
4.3.3.7.页面创建UploadImg.vue
<template>
  <div>
    <el-form>
      <el-form-item label="上传图片">
        <el-upload
          list-type="picture-card"
          :multiple="false"
          :action="uploadUrl"
          :limit="1"
          :on-success="onUploadSuccessIdCard"
        >
          <i class="el-icon-plus"></i>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "UploadImg",
  data() {
    return {
      dialogImageUrl: "",
      file_id: "",
      dialogVisible: false,
      uploadUrl: "http://localhost:22100/filesystem/uploadFile", //文件上传地址
      datas: {},
    };
  },
  methods: {
    onUploadSuccessIdCard(response) {
      this.file_id = response.data.fileId;
      this.datas = response.data;
      this.dialogImageUrl = "http://192.168.229.141/" + response.data.filePath;
    },
  },
};
</script>

<style scoped>
</style>
4.3.3.8.修改App.vue
<template>
  <div id="app">
    <HelloWorld />
    <!-- 导航链接 -->
    <!-- 路由内容展示 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
</style>
4.3.3.9.创建路由配置router/index.js
//在路由文件router/index.js中配置
// 该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";

//引入组件
import UploadImg from '@/components/UploadImg'//上传页

//创建路由
const routes = [
  //定义路由
  {
    path: '/',
    name: 'UploadImg',
    component: UploadImg
  },
]

//创建并暴露一个路由器
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router
4.3.3.10.课程图片浏览
npm rn serve

在这里插入图片描述

4.3.4.管理系统前端【Vue3】项目

4.3.4.1.axios模块
4.3.4.1.1.安装axios模块

vue-axios|axios中文网:http://www.axios-js.com/zh-cn/docs/vue-axios.html
axios官网:https://www.axios-http.cn/docs/intro

npm install --save axios vue-axios

//报错时使用--legacy-peer-deps
npm install --save axios vue-axios --legacy-peer-deps
4.3.4.1.2.在入口文件main.js中配置axios
#在入口文件main.js中配置
import { createApp } from 'vue'
import App from './App.vue'

//引入axios
import axios from 'axios'
//引入VueAxios
//安装VueAxios模块后,不需要在每个组件中单独导入axios,只需将axios请求改为this.axios
import VueAxios from 'vue-axios'

const app = createApp(App);

//使用Vue.use来注册安装插件
app.use(VueAxios, axios)

app.mount('#app')

//createApp(App).mount('#app')
按照这个顺序分别引入这三个文件: vue, axios and vue-axios
4.3.4.2.关闭Vue的生产提示
#在入口文件main.js中配置
//关闭Vue的生产提示
app.config.productionTip = false
4.3.4.3.vue项目中无router文件夹,vue安装路由

新建一个router文件夹,在文件夹下新建一个index.js文件

在这里插入图片描述

4.3.4.4.引入Vue Router路由Vue.js【v.4x】

Vue3使用v.4x
Vue Router官网【v4.x】:https://router.vuejs.org/zh/
更新记录【4.x】https://github.com/vuejs/router/releases

//vue-router4x 适用于 vue3
npm install vue-router@4

//指定版本号
npm install vue-router@4.2.5 --save
import { createApp } from 'vue'
import App from './App.vue'

//引入路由器
import router from './router/index'

const app = createApp(App);

//使用Vue.use来注册安装插件
app.use(router)

app.mount('#app')

在这里插入图片描述

4.3.4.5.引入Element Plus组件库【Vue3 版本】

Element Plus组件库官网:https://element-plus.gitee.io/zh-CN/

npm install element-plus --save
// main.ts
import { createApp } from 'vue'
//引入Element Plus组件库
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')
4.3.4.6.main.js完整版
import { createApp } from 'vue'
import App from './App.vue'

//引入axios
import axios from 'axios'
//引入VueAxios
//安装VueAxios模块后,不需要在每个组件中单独导入axios,只需将axios请求改为this.axios
import VueAxios from 'vue-axios'
//新建一个router文件夹,在文件夹下新建一个index.js文件
//引入路由器
import router from './router/index'
//引入Element Plus组件库
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

const app = createApp(App);
//关闭Vue的生产提示
app.config.productionTip = false

//使用Vue.use来注册安装插件
app.use(VueAxios, axios)
app.use(router)
app.use(ElementPlus)

app.mount('#app')
4.3.4.7.页面创建UploadImg.vue
<template>
  <div>
    <el-form>
      <el-form-item label="上传图片">
        <el-upload
          list-type="picture-card"
          :multiple="false"
          :action="uploadUrl"
          :limit="1"
          :on-success="onUploadSuccessIdCard"
        >
          <i class="el-icon-plus"></i>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "UploadImg",
  data() {
    return {
      dialogImageUrl: "",
      file_id: "",
      dialogVisible: false,
      uploadUrl: "http://localhost:22100/filesystem/uploadFile", //文件上传地址
      datas: {},
    };
  },
  methods: {
    onUploadSuccessIdCard(response) {
      this.file_id = response.data.fileId;
      this.datas = response.data;
      this.dialogImageUrl = "http://192.168.229.141/" + response.data.filePath;
    },
  },
};
</script>

<style scoped>
</style>
4.3.4.8.修改App.vue
<template>
  <div id="app">
    <HelloWorld />
    <!-- 导航链接 -->
    <!-- 路由内容展示 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
</style>
4.3.4.9.创建路由配置router/index.js
import { createRouter, createWebHistory } from 'vue-router'

const routerHistory = createWebHistory(process.env.BASE_URL)

import UploadImg from '@/components/UploadImg'
// 定义路由
const routes = [
  {
    path: '/',
    name: 'UploadImg',
    component: UploadImg
  },
]

// 创建路由器
const router = createRouter({
  history: routerHistory,
  routes: routes
})


export default router;
4.3.4.10.页面效果
npm rn serve

在这里插入图片描述

五、总结

  • 分布式文件系统是通过网络将单机上的文件系统组成一个网络文件系统
  • 分布式文件系统主要应用在大型互联网项目中,实现图片存储、音视频存储等服务
  • 分布式文件系统的优点:可以快速扩容存储,提高文件访问速度
  • fastDFS的工作原理:fastDFS由tracker和storage组成,它们都可以部署集群。tracker负责调度,storage负责存储
  • fastDFS存取文件方法,客户端与fastDFS采用socket协议通信,采用官方提供的java版本的fastDSF-client快速开发
  • 能够动手搭建一个fastDSF文件服务器

endl

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第4讲 小程序首页实现

首页 create.vue <template><view class"vote_type"><view class"vote_tip_wrap"><text class"type_tip">请选择投票类型</text><!-- <text class"share">&#xe739;分享给朋友</text&g…

如何在C# Windows Forms应用程序中实现控件之间的连接线

帮我实现绘图工具多个控件连接线&#xff0c;请用c#代码实现 实现绘图工具中多个控件之间的连接线功能&#xff0c;可以通过以下几个步骤来进行&#xff1a; 定义连接线的数据模型&#xff1a;首先需要定义一个模型来表示连接线&#xff0c;这个模型应该包含起点和终点的坐标。…

JavaScript中有哪些不同的数据类型

在 JavaScript 中&#xff0c;数据类型是一种用来表示数据的分类&#xff0c;它决定了我们可以对这个数据类型执行哪些操作。在 JavaScript 中有以下几种不同的数据类型&#xff1a; 基本数据类型 字符串 (String)&#xff1a;表示一组字符&#xff0c;可以使用引号&#xff08…

Pytorch的可视化

1 使用 wandb进行可视化训练过程 本文章将从wandb的安装、wandb的使用、demo的演示进行讲解。 1.1 如何安装wandb&#xff1f; wandb的安装比较简单&#xff0c;在终端中执行如下的命令即可&#xff1a; pip install wandb在安装完成之后&#xff0c;我们需要&#xff0c;去…

华为机考入门python3--(13)牛客13-句子逆序

分类&#xff1a;列表 知识点&#xff1a; 列表逆序&#xff08;和字符串逆序是一样的&#xff09; my_list[::-1] 题目来自【牛客】 def reverse_sentence(sentence): # 将输入的句子分割words sentence.split() # 将单词逆序排列 words words[::-1] # 将单词用空…

DarkSide针对VMware EXSI系统进行加密

前言 最近黑客组织利用DarkSide勒索病毒对Colonial Pipeline 发起勒索攻击&#xff0c;国内外各大安全厂商和安全媒体也都有相关报道&#xff0c;DarkSide勒索软件是从2020年8月出现&#xff0c;并以(RAAS)勒索即服务的商业模式进行运作&#xff0c;此勒索病毒不仅可以部署基于…

【Chrono Engine学习总结】5-sensor-5.1-sensor基础并创建一个lidar

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 1、Sensor模块 Sensor模块是附加模块&#xff0c;需要单独安装。参考&#xff1a;【Chrono Engine学习总结】1-安装配置与程序运行 Sensor Module Tutorial Sensor …

MySQL数据库⑧_索引(概念+理解+操作)

目录 1. 索引的概念和价值 1.1 索引的概念 1.2 索引的价值 2. 磁盘的概念 2.1 磁盘的结构 2.2 操作系统与磁盘交互的基本单位 2.3 MySQL与磁盘交互的基本单位 3. 索引的理解 3.1 主键索引现象和推导 3.2 索引采用的数据结构&#xff1a;B树 3.3 聚簇索引和非聚簇索引…

Java并发基础:DelayQueue全面解析!

内容概要 DelayQueue类专为处理延迟任务设计&#xff0c;它允许开发者将任务与指定的延迟时间关联&#xff0c;并在任务到期时自动处理&#xff0c;从而避免了不必要的轮询和资源浪费&#xff0c;此外&#xff0c;DelayQueue内部基于优先队列实现&#xff0c;确保最先到期的任…

蓝桥杯每日一题------背包问题(三)

前言 之前求的是在特点情况下选择一些物品让其价值最大&#xff0c;这里求的是方案数以及具体的方案。 背包问题求方案数 既然要求方案数&#xff0c;那么就需要一个新的数组来记录方案数。动态规划步骤如下&#xff0c; 定义dp数组 第一步&#xff1a;缩小规模。考虑n个物品…

ubuntu22.04@laptop OpenCV Get Started: 007_color_spaces

ubuntu22.04laptop OpenCV Get Started: 007_color_spaces 1. 源由2. 颜色空间2.1 RGB颜色空间2.2 LAB颜色空间2.3 YCrCb颜色空间2.4 HSV颜色空间 3 代码工程结构3.1 C应用Demo3.2 Python应用Demo 4. 重点分析4.1 interactive_color_detect4.2 interactive_color_segment4.3 da…

【MySQL】学习约束和使用图形化界面创建表

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-iqtbME2KmWpQFQSt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

调用讯飞火星AI大模型WebAPI

调用讯飞火星AI大模型 记录一次调用讯飞AI大模型的过程 官方文档 首先&#xff0c;去官网申请资格&#xff0c;获得必要秘钥等 再编写url&#xff0c;该url存在编码要求&#xff0c;具体看官网url编写 具体代码如下&#xff1a; getWebsocketUrl() {return new Promise((resol…

Peter算法小课堂—区间模型

Peter Pan来啦…… 最大不重叠区间数 二话不说&#xff0c;先来一道题 大家想想怎么贪心&#xff1f;我们可以将每一个美食摊位抽象成一个区间&#xff0c;区间左端点为开始排队时间&#xff0c;右端点为结束排队时间。其中&#xff0c;时间信息可以用数轴表示。 额……我们…

golang集成sentry: go-redis

网上没有找到go-redis集成sentry的库&#xff0c; 所以我简单实现了一个 代码&#xff1a; https://github.com/Shujie-Tan/go-redis-sentry 使用方法&#xff1a; import (redis_sentry "github.com/Shujie-Tan/go-redis-sentry" ) rdb : redis.NewClient(&re…

linux---内存管理

一 虚拟内存 即使是现代操作系统中&#xff0c;内存依然是计算机中很宝贵的资源&#xff0c;看看你电脑几个T固态硬盘&#xff0c;再看看内存大小就知道了。 为了充分利用和管理系统内存资源&#xff0c;Linux采用虚拟内存管理技术&#xff0c;利用虚拟内存技术让每个进程都有…

Linux网络编程——tcp套接字

文章目录 主要代码关于构造listen监听accepttelnet测试读取信息掉线重连翻译服务器演示 本章Gitee仓库&#xff1a;tcp套接字 主要代码 客户端&#xff1a; #pragma once#include"Log.hpp"#include<iostream> #include<cstring>#include<sys/wait.h…

162基于matlab的多尺度和谱峭度算法对振动信号进行降噪处理

基于matlab的多尺度和谱峭度算法对振动信号进行降噪处理&#xff0c;选择信号峭度最大的频段进行滤波&#xff0c;输出多尺度谱峭度及降噪结果。程序已调通&#xff0c;可直接运行。 162 matlab 信号处理 多尺度谱峭度 (xiaohongshu.com)

Acwing---844.走迷宫

走迷宫 1.题目2.基本思想3.代码实现 1.题目 给定一个 nm 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1&#xff0c;其中 0 表示可以走的路&#xff0c;1 表示不可通过的墙壁。最初&#xff0c;有 一个人位于左上角 (1,1)处&#xff0c;已知该…

实景剧本杀小程序:创新体验,沉浸式推理乐趣

随着科技的飞速发展&#xff0c;人们对于娱乐方式的追求也在不断升级。传统的桌面剧本杀游戏已经不能满足玩家的需求&#xff0c;他们渴望更加真实、刺激的游戏体验。正是这种需求推动下&#xff0c;实景剧本杀小程序应运而生&#xff0c;为玩家带来前所未有的推理乐趣。 实景…