当前位置: 首页 > news >正文

分布式文件系统 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 {//测试文件上传@Testpublic void Upload() {//通过fastDSF的client代码访问tracker和storagetry {//加载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.pngProcess 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 {//测试文件查询@Testpublic void Query() {//通过fastDSF的client代码访问tracker和storagetry {//加载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.pngString 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.pngProcess finished with exit code 0

3.2.4.文件下载

3.2.4.1.文件下载代码实现
public class TestFastDFSDownload {//测试文件下载@Testpublic void Download() {//通过fastDSF的client代码访问tracker和storagetry {//加载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.pngString 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 = -743583210Process finished with exit code 0

3.2.5.文件删除

3.2.5.1.文件删除代码实现
public class TestFastDFSDelete {//测试文件删除@Testpublic 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.pngString 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: 40at 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 -versionmvn 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 {@Overridepublic 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")@ResponseBodypublic Result upload(@RequestParam("file") MultipartFile file) throws IOException {//将文件先存储在web服务器上(本机),在调用fastDFS的client将文件上传到 fastDFS服务器FileSystem fileSystem = new FileSystem();//文件原始名称String originalFilename = file.getOriginalFilename();//文件扩展名比如22.jpgString extension = originalFilename.substring(originalFilename.lastIndexOf("."));log.info("文件扩展名后缀 = {}", extension);//.jpgString filenameExtension = StringUtils.getFilenameExtension(originalFilename);log.info("文件类型 = {}", filenameExtension);//jpgif (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和storagetry {//加载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")@ResponseBodypublic 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.jpgString extension = originalFilename.substring(originalFilename.lastIndexOf("."));log.info("文件扩展名后缀 = {}", extension);//.jpgString filenameExtension = StringUtils.getFilenameExtension(originalFilename);log.info("文件类型 = {}", filenameExtension);//jpgif (filenameExtension == null) {return Result.error("此文件没有文件扩展名");}//通过fastDSF的client代码访问tracker和storagetry {//加载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: 22100orange-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 = axiosnew 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-uploadlist-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-uploadlist-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

相关文章:

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

分布式文件系统 SpringBootFastDFSVue.js【一】 一、分布式文件系统1.1.文件系统1.2.什么是分布式文件系统1.3.分布式文件系统的出现1.3.主流的分布式文件系统1.4.分布式文件服务提供商1.4.1.阿里OSS1.4.2.七牛云存储1.4.3.百度云存储 二、fastDFS2.1.fastDSF介绍2.2.为什么要使…...

【PyQt】11-QTextEdit、QPushButton

文章目录 前言一、文本输入-QTextEdit1.1 代码1.2 运行结果 二、QPushButton2.1.1 按钮上添加文本2.1.2 按键的弹跳效果2.1.3 两个信号可以绑定一个槽。2.1.4 带图标的按键运行结果 2.1.5 按键不可用以及回车默认完整代码2.2 单选按键控件运行结果 2.3 复选框&#xff08;多选框…...

初识webpack(二)解析resolve、插件plugins、dev-server

目录 (一)webpack的解析(resolve) 1.resovle.alias 2.resolve.extensions 3.resolve.mainFiles (二) plugin插件 1.CleanWebpackPlugin 2.HtmlWebpackPlugin 3.DefinePlugin (三)webpack-dev-server 1.开启本地服务器 2.HMR模块热替换 3.devServer的更多配置项 (…...

什么是自编码器Auto-Encoder?

来源&#xff1a;https://www.bilibili.com/video/BV1Vx411j78H/?spm_id_from333.1007.0.0&vd_sourcef66cebc7ed6819c67fca9b4fa3785d39 为什么要压缩呢&#xff1f; 让神经网络直接从上千万个神经元中学习是一件很吃力的事情&#xff0c;因此通过压缩提取出原图片中最具代…...

openGauss学习笔记-219 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-网络

文章目录 openGauss学习笔记-219 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-网络219.1 查看网络状况 openGauss学习笔记-219 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-网络 获取openGauss节点的CPU、内存、I/O和网络资源使用情况&#xff0c;确认这些资源…...

SAP PP学习笔记- 豆知识01 - 怎么查询既存品目

SAP系统当中已经有哪些品目要怎么查询呢&#xff1f; 1&#xff0c;MM60 品目一览 这里可以输入Plant&#xff0c;然后可以查询该工厂的所有品目。 2&#xff0c;SE16 > MARA MARA 品目一般Data&#xff0c;存放的是品目基本信息。 如果要查询该品目属于哪个Plant&#x…...

相机的机身马达有什么用?

新手疑问&#xff1a; 为什么我的尼康D3200相机明明拥有拍视频能力&#xff0c;但是拍摄视频时却不能对焦 科普时间 那是因为你的相机缺少机身马达&#xff0c;并且你所使用的镜头也没有马达!机身马达是用于给镜头提供对焦动力的装置。它的作用是使相机具备自动对焦功能。如…...

拿捏c语言指针(上)

目录 前言 ​编辑 指针 内存与地址 计算机常见单位 理解编址 取地址&#xff0c;指针变量&#xff0c;解引用 取地址 指针变量 解引用 指针变量大小 指针类型的作用 char*解引用后 指针-整数 应用 void*指针 const修饰指针变量 const修饰普通变量 const修饰指…...

JVM指令手册

栈和局部变量操作将常量压入栈的指令 aconst_null 将null对象引用压入栈 iconst_m1 将int类型常量-1压入栈 iconst_0 将int类型常量0压入栈 iconst_1 将int类型常量1压入操作数栈 iconst_2 将int类型常量2压入栈 iconst_3 将int类型常量3压入栈 iconst_4 将int类型常量4…...

Linux之多线程

目录 一、进程与线程 1.1 进程的概念 1.2 线程的概念 1.3 线程的优点 1.4 线程的缺点 1.5 线程异常 1.6 线程用途 二、线程控制 2.1 POSIX线程库 2.2 创建一个新的线程 2.3 线程ID及进程地址空间布局 2.4 线程终止 2.5 线程等待 2.6 线程分离 一、进程与线程 在…...

TestNG invocationCount属性

有时我们会遇到这样的问题&#xff0c;比如如何多次运行一个测试用例&#xff1f;invocationCount是这个问题的答案。在这篇文章中&#xff0c;我们将讨论在TestNG中与Test annotation一起使用的invocationCount属性。 这个属性有什么作用&#xff0c;或者调用计数有什么用&am…...

关于maven项目中无法通过邮件服务器发送邮件的补充解决方案

1、问题及解决方法 我的一篇文章中提到使用代码发送电子邮件&#xff0c;但是maven项目中无法执行成功&#xff0c;现在我找到了解决办法&#xff0c;只要引入依赖时同时引入下面两个依赖就行了&#xff0c;我无法找到原因主要是使用单元测试方法运行&#xff0c;它居然不报错&…...

树形dp 笔记

树的最长路径 给定一棵树&#xff0c;树中包含 n 个结点&#xff08;编号1~n&#xff09;和 n−1 条无向边&#xff0c;每条边都有一个权值。 现在请你找到树中的一条最长路径。 换句话说&#xff0c;要找到一条路径&#xff0c;使得使得路径两端的点的距离最远。 注意&…...

2024-02-08 Unity 编辑器开发之编辑器拓展1 —— 自定义菜单栏

文章目录 1 特殊文件夹 Editor2 在 Unity 菜单栏中添加自定义页签3 在 Hierarchy 窗口中添加自定义页签4 在 Project 窗口中添加自定义页签5 在菜单栏的 Component 菜单添加脚本6 在 Inspector 为脚本右键添加菜单7 加入快捷键8 小结 1 特殊文件夹 Editor ​ Editor 文件夹是 …...

typescript中的Omit排除类型及Pick取想要的属性

Omit 的使用:排除类型 type OmitUser {name: string,age: number,sex:string } type newOmit Omit<OmitUser, sex>// 定义一个对象并将其类型设置为 newOmit const example: newOmit {name: "John",age: 30 };console.log( Omit 的使用:排除类型 , example…...

MATLAB计算极限和微积分

一.函数与极限 计算极限&#xff1a;lim(3*x^2/(2x1))&#xff0c;x分别趋于0和1&#xff0c;代码如下&#xff1a; syms x; limit(3*x*x/(2*x1),x,0) limit(3*x*x/(2*x1),x,1) 结果分别为0和1&#xff1a; 1.计算双侧极限 计算极限&#xff1a;lim(3*x^2/(2x1))&#xff0…...

在数组中插入元素

问题&#xff1a;假设有一个数组{1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5}&#xff0c;如果我们要在3之后插入一个数&#xff08;520&#xff09;&#xff0c;这该怎么办呢&#xff1f; 思路&#xff1a;要想在以元素3之后插入一个元素&#xff0c;我们先要做…...

【计算机网络】物理层|传输介质|物理层设备|宽带接入技术

目录 一、思维导图 二、传输介质 1.传输介质——导引型 2.传输介质——非导引型​编辑 三、物理层设备 1.物理层设备&#xff1a;中继器&集线器 2.宽带接入技术&#xff08;有线&#xff09; ​编辑 四、趁热打铁☞习题训练 五、物理层总思维导图 推荐 前些天发现…...

TCP和UDP面试题提问

TOC TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是两种计算机网络通信协议&#xff0c;它们在网络通信中起着不同的作用。 TCP TCP 是面向连接的协议&#xff0c;它在数据传输之前需要在发送端和接收端建立一条连接。TCP 提供可靠…...

网安常用的三个攻击方式

1.渗透测试执行标准&#xff08;PTES&#xff09; 渗透测试执行标准由7个部分组成&#xff0c;包括前期交互、情报收集、威胁建模、漏洞分析、渗透利用、后渗透、撰写报告。在中国&#xff0c;渗透测试必须经过授权&#xff0c;否则就违背了网络安全法。前期交互主要指开展渗透…...

C++面向对象程序设计-北京大学-郭炜【课程笔记(二)】

C面向对象程序设计-北京大学-郭炜【课程笔记&#xff08;二&#xff09;】 1、结构化程序设计结构化程序设计的不足 2、面向对象的程序设计2.1、面向对象的程序设计2.2、从客观事物抽象出类2.3、对象的内存分配2.4、对象之间的运算2.5、使用类的成员变量和成员函数用法1&#x…...

IDEA Ultimate下载(采用JetBrain学生认证)

IDEA Ultimate版本下载 Ulitmate是无限制版&#xff08;解锁所有插件&#xff0c;正版需要付费。学生可以免费申请许可&#xff09;Community是开源社区版本&#xff08;部分插件不提供使用&#xff0c;比如Tomcat插件。免费&#xff09; 我们将通过学生认证获取免费版。 Je…...

Matplotlib plt.plot数据可视化应用案例

Matplotlib 是 Python 中一个非常流行的绘图库&#xff0c;它允许用户创建各种静态、动态、交互式的图表和可视化。plt.plot() 是 Matplotlib 中用于绘制二维数据的基本函数。 下面是一个使用 plt.plot() 的简单数据可视化应用案例&#xff1a; 案例&#xff1a;绘制正弦和余…...

ES实战--集群扩展

查看ES集群状态: GET /_cluster/health?prettytrue当一个节点加入集群的时候,ES会自动地尝试将分片在所有节点上进行均匀分配. 如果更多的节点加入集群,ES将试图在所有节点上均匀分配分片数量.这样每一个新加入的节点都能通过部分数据来分担负载 第二个节点发现第一个节点,并…...

【重要】django默认生成的表的意思记录

accounts_userprofile: 这是与用户相关的个人资料表&#xff0c;通常包含用户的额外信息&#xff0c;比如头像、个人描述等。 accounts_userprofile_groups: 这是用户个人资料和用户组之间的关联表&#xff0c;用于记录用户属于哪些用户组。 accounts_userprofile_user_permiss…...

12.3 OpenGL顶点后处理:平面着色

平面着色 Flatshading Flat shading (平面着色)是一种简化渲染技术&#xff0c;它在光栅化阶段将一个图元&#xff08;primitive&#xff09;的所有顶点赋予相同的颜色或其它输出变量的值。这些赋予的值来自于该图元的“引发顶点”&#xff08;provoking vertex&#xff09;。…...

实验5-6 使用函数判断完全平方数

本题要求实现一个判断整数是否为完全平方数的简单函数。 函数接口定义&#xff1a; int IsSquare( int n ); 其中n是用户传入的参数&#xff0c;在长整型范围内。如果n是完全平方数&#xff0c;则函数IsSquare必须返回1&#xff0c;否则返回0。 裁判测试程序样例&#xff1…...

AI 或许真的能助力中产阶级重塑辉煌 [译]

原文&#xff1a;AI Could Actually Help Rebuild The Middle Class 作者&#xff1a;DAVID AUTOR 译文&#xff1a;AI 或许真的能助力中产阶级重塑辉煌 作者&#xff1a;宝玉 人工智能&#xff08;AI&#xff09;并不一定会夺走我们的工作。相反&#xff0c;它为我们提供了一个…...

C#利用接口实现选择不同的语种

目录 一、涉及到的知识点 1.接口定义 2.接口具有的特征 3.接口通过类继承来实现 4.有效使用接口进行组件编程 5.Encoding.GetBytes(String)方法 &#xff08;1&#xff09;检查给定字符串中是否包含中文字符 &#xff08;2&#xff09;编码和还原前后 6.Encoding.GetS…...

设计模式-适配器模式 Adapter

适配器模式 (Adapter) (重点) 适配器设计模式&#xff08;Adapter Design Pattern&#xff09;是一种结构型设计模式&#xff0c;用于解决两个不兼容接口之间的问题。适配器允许将一个类的接口转换为客户端期望的另一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的…...

算法训练day29Leetcode491递增子序列46全排列47全排列Ⅱ

491 递增子序列 题目描述 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一…...

内网穿透与搭建私人服务器

前言 内网穿透是一种技术&#xff0c;允许用户从外部网络访问内部私有网络中的服务器或设备。这对于想要从任何地方访问家中或办公室内部网络资源的用户非常有用。以下是为初学者准备的关于如何实现内网穿透以及搭建自己的私人服务器的详细指南。 在这个数字化时代&#xff0c;…...

交大论文下载器

原作者地址&#xff1a; https://github.com/olixu/SJTU_Thesis_Crawler 问题&#xff1a; http://thesis.lib.sjtu.edu.cn/的学位论文下载系统&#xff0c;该版权保护系统用起来很不方便&#xff0c;加载起来非常慢&#xff0c;所以该下载器实现将网页上的每一页的图片合并…...

全栈笔记_浏览器扩展篇(manifest.json文件介绍)

manifest.json介绍 是web扩展技术必不可少的插件配置文件,放在根目录作用: 指定插件的基本信息 name:名称manifest_version:manifest.json文件的版本号,可以写2或3version:版本description:描述定义插件的行为: browser_action:添加一个操作按钮到浏览器工具栏,点击按…...

蓝桥杯每日一题(python)

##斐波那契数列的应用 --- 题目斐波那契 题目&#xff1a; 如果数组 A (a0, a1, , an−1) 满足以下条件&#xff0c;就说它是一个斐波那契数组&#xff1a; 1. n ≥ 2&#xff1b; 2. a0 a1&#xff1b; 3. 对于所有的 i(i ≥ 2)&#xff0c;都满足 ai ai−1 ai−2…...

【Vue】工程化开发脚手架Vue CLI

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Vue⛺️稳重求进&#xff0c;晒太阳 工程化开发&脚手架Vue CLI 基本介绍 Vue Cli是Vue官方提供的一个全局命令工具 可以帮助我们快速创建一个开发Vue项目的标准化基础架子【集成了we…...

嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第三天-ARM Linux ADC和触摸屏开发 (物联技术666)

链接&#xff1a;https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd1688 提取码&#xff1a;1688 教学内容&#xff1a; 1、ADC S3C2440的A/D转换器包含一个8通道的模拟输入转换器&#xff0c;可以将模拟输入信号转换成10位数字编码。 在A/D转换时钟频率为2.5MHz时&…...

LeetCode “AddressSanitizer:heat-use-after-free on address“问题解决方法

heat-use-after-free &#xff1a; 访问堆上已经被释放的内存地址 现象&#xff1a;同样代码在LeetCode上报错&#xff0c;但是自己在IDE手动打印并不会报错 个人猜测&#xff0c;这个bug可能来源于LeetCode后台输出打印链表的代码逻辑问题。 问题描述 题目来自LeetCode的8…...

幸运彩票

L1-6 幸运彩票 分数 15 作者 陈越 单位 浙江大学 彩票的号码有 6 位数字&#xff0c;若一张彩票的前 3 位上的数之和等于后 3 位上的数之和&#xff0c;则称这张彩票是幸运的。本题就请你判断…...

搭建yum仓库服务器

安装 1.安装linux 1.1安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel 1.2下载 cd /opt/nginx wget http://nginx.org/download/nginx-1.25.3.tar.gz 1.3解压 tar -xvf nginx-1.25.3.tar.gz 1.4配置 cd nginx-1.25.3 ./configure --pre…...

贪心算法练习day1

练习1--翻硬币 1&#xff09;题目及要求 2&#xff09;解题思路 输入的是字符串&#xff0c;要想将两组字符串进行一一对比&#xff0c;需要将字符串转换成字符数组&#xff0c;再使用for循环依次遍历字符数组&#xff0c;进行比对。 输入两行字符串&#xff0c;转换成两个字…...

[VulnHub靶机渗透] WestWild 1.1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…...

如何使用 ControlValueAccessor 在 Angular 中创建自定义表单控件

简介 在 Angular 中创建表单时&#xff0c;有时您希望拥有一个不是标准文本输入、选择或复选框的输入。通过实现 ControlValueAccessor 接口并将组件注册为 NG_VALUE_ACCESSOR&#xff0c;您可以将自定义表单控件无缝地集成到模板驱动或响应式表单中&#xff0c;就像它是一个原…...

视频讲解:优化柱状图

你好&#xff0c;我是郭震 AI数据可视化 第三集&#xff1a;美化柱状图&#xff0c;完整视频如下所示&#xff1a; 美化后效果前后对比&#xff0c;前&#xff1a; 后&#xff1a; 附完整案例源码&#xff1a; util.py文件 import platformdef get_os():os_name platform.syst…...

OpenAI宣布ChatGPT新增记忆功能;谷歌AI助理Gemini应用登陆多地区

&#x1f989; AI新闻 &#x1f680; OpenAI宣布ChatGPT新增记忆功能&#xff0c;可以自由控制内存&#xff0c;提供个性化聊天和长期追踪服务 摘要&#xff1a;ChatGPT新增的记忆功能可以帮助AI模型记住用户的提问内容&#xff0c;并且可以自由控制其内存。这意味着用户不必…...

Solidworks:平面草图练习

继续练习平面草图&#xff0c;感觉基本入门了。...

React18原理: 渲染与更新时的重点关注事项

概述 react 在渲染过程中要做很多事情&#xff0c;所以不可能直接通过初始元素直接渲染还需要一个东西&#xff0c;就是虚拟节点&#xff0c;暂不涉及React Fiber的概念&#xff0c;将vDom树和Fiber 树统称为虚拟节点有了初始元素后&#xff0c;React 就会根据初始元素和其他可…...

嵌入式I2C 信号线为何加上拉电阻(图文并茂)

IIC 是一个两线串行通信总线&#xff0c;包含一个 SCL 信号和 SDA 信号&#xff0c;SCL 是时钟信号&#xff0c;从主设备发出&#xff0c;SDA 是数据信号&#xff0c;是一个双向的&#xff0c;设备发送数据和接收数据都是通过 SDA 信号。 在设计 IIC 信号电路的时候我们会在 SC…...

Vite 5.0 正式发布

11 月 16 日&#xff0c;Vite 5.0 正式发布&#xff0c;这是 Vite 道路上的又一个重要里程碑&#xff01;Vite 现在使用 Rollup 4&#xff0c;这已经代表了构建性能的大幅提升。此外&#xff0c;还有一些新的选项可以改善开发服务器性能。 Vite 4 发布于近一年前&#xff0c;它…...

嵌入式STM32 单片机 GPIO 的工作原理详解

STM32的 GPIO 介绍 GPIO 是通用输入/输出端口的简称&#xff0c;是 STM32 可控制的引脚。GPIO 的引脚与外部硬件设备连接&#xff0c;可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 以 STM32F103ZET6 芯片为例子&#xff0c;该芯片共有 144 脚芯片&#xff0c…...