首页
关于
推荐
CSDN
Search
1
文件上传下载-io-流的理解-笔记
128 阅读
2
vue循环指令el-table-column展示图片src路径拼接
121 阅读
3
正则表达式,将字符串分割两部分
111 阅读
4
MySQL数据库练习【一】
109 阅读
5
MySQL数据库练习【三】
92 阅读
默认分类
Mysql
Java基础
一天一练
Mongodb
Nginx
Docker
FastDFS
面试题
云计算基础
linux基础
shell脚本
实验
工具
基础命令
redis
zookeeper
部署
案例
登录
Search
标签搜索
vue
Mysql
IO
面试题
良辰美景好时光
累计撰写
67
篇文章
累计收到
0
条评论
首页
栏目
默认分类
Mysql
Java基础
一天一练
Mongodb
Nginx
Docker
FastDFS
面试题
云计算基础
linux基础
shell脚本
实验
工具
基础命令
redis
zookeeper
部署
案例
页面
关于
推荐
CSDN
搜索到
5
篇与
的结果
2024-07-25
分布式文件系统 SpringBoot+FastDFS+Vue.js
@TOC一、分布式文件系统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分布式文件系统)FastDFS1.4.分布式文件服务提供商1.4.1.阿里OSS对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。1.4.2.七牛云存储1.4.3.百度云存储二、fastDFS2.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.为什么要使用fastDFSNFS、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)TrackerTracker Server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server提供文件上传服务。可以将tracker称为追踪服务器或调度服务器。FastDFS集群中的Tracker server可以有多台,Tracker server之间是相互平等关系同时提供服务,Tracker server不存在单点故障。客户端请求Tracker server采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。2)StorageStorage 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查询可用的storagetracker向client返回信息(storage的ip和端口)client向storage上传文件(file content和metadata)storage生成一个file_idstorage将上传内容写入磁盘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查询可用的storagetracker向client返回信息(storage的ip和端口)client向storage提交信息file_id(组名、路径、文件名)storage查看文件storage返回file_content给clienttracker根据请求的文件路径即文件ID 来快速定义文件。比如请求下边的文件:通过组名tracker能够很快的定位到客户端需要访问的存储服务器组是group1,并选择合适的存储服务器提供客户端访问。存储服务器根据“文件存储虚拟磁盘路径”和“数据文件两级目录”可以很快定位到文件所在目录,并根据文件名找到客户端需要访问的文件。三、fastDFS入门3.1.fastDFS安装与配置详见:https://blog.csdn.net/qq_45740503/article/details/1360867313.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:221223.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 03.2.2.3.测试上传结果-访问cd /home/fastdfs/fdfs_storage/data/00/00http://192.168.229.141/group1/M00/00/00/wKjljWXHAauARHa2AAWwwNOt0hY257.png3.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 03.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 03.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 03.2.5.3.服务器查看3.3.recv body length: 70 is not correct, expect length: 40network_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.313.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 密码:deca3.3.2.1.下载解压3.3.2.2.使用maven从源码安装mvn -version mvn clean install3.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:221224.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.ymlserver: port: 22100 orange-fastdfs: #文件上传临时目录 upload_location: D:\\image\\upload\\ # linux临时文件目录 # mkdir -p /data/tmp/updatefile spring: servlet: multipart: location: /data/tmp/updatefile4.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.htmlaxios官网:https://www.axios-http.cn/docs/intronpm install --save axios vue-axios //报错时使用--legacy-peer-deps npm install --save axios vue-axios --legacy-peer-deps4.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-axios4.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 = false4.3.3.3.vue项目中无router文件夹,vue安装路由新建一个router文件夹,在文件夹下新建一个index.js文件4.3.3.4.引入Vue Router路由Vue.js【v.3x】Vue2使用v.3xVue 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-CNnpm 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 router4.3.3.10.课程图片浏览npm rn serve4.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.htmlaxios官网:https://www.axios-http.cn/docs/intronpm install --save axios vue-axios //报错时使用--legacy-peer-deps npm install --save axios vue-axios --legacy-peer-deps4.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-axios4.3.4.2.关闭Vue的生产提示#在入口文件main.js中配置 //关闭Vue的生产提示 app.config.productionTip = false4.3.4.3.vue项目中无router文件夹,vue安装路由新建一个router文件夹,在文件夹下新建一个index.js文件4.3.4.4.引入Vue Router路由Vue.js【v.4x】Vue3使用v.4xVue 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 --saveimport { 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.jsimport { 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文件服务器使用Java程序进行图片的上传、查询、下载就完成了,无论是上传、查询、还是下载总结为:加载配置文件 获取TrackerClient对象 通过TrackerClient对象来获取TrackerServer对象 通过TrackerClient对象来获取StorageServer对象 通过TrackerServer对象和StorageServer对象来获取StorageClient1对象 上传的话执行upload_file1方法,查询执行query_file_info方法,下载执行download_file1方法@TOC六、实现上传功能并展示数据6.1.创建数据库CREATE DATABASE IF NOT EXISTS fastdfs CHARACTER SET utf8mb4; show databases; USE fastdfs; CREATE TABLE `fast_dfs_file` ( `id` bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键', `file_id` varchar(200) NOT NULL COMMENT '文件的fileId', `file_path` varchar(200) NOT NULL COMMENT '文件路径', `file_size` bigint(25) NOT NULL COMMENT '文件大小', `file_name` varchar(25) NOT NULL COMMENT '文件名', `ext` varchar(30) NOT NULL COMMENT '文件的扩展名,不包含(.)', `file_type` varchar(50) NOT NULL COMMENT '文件类型', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '最后修改时间' ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='文件表'; CREATE TABLE `fast_dfs_file_type` ( `id` bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键', `file_type` varchar(50) NOT NULL COMMENT '文件类型', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '最后修改时间' ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='文件类型表'; insert into fast_dfs_file_type(file_type)values('image/jpeg'),('image/png'),('image/jpg');6.2.创建spring boot项目fastDFS-java6.3.引入依赖<?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 https://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>2.7.5</version> <relativePath/> </parent> <groupId>com.orange</groupId> <artifactId>fastDFS-java</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!--web起步依赖--> <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> <!--mybatis起步依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--springboot单元测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--pagehelper分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> <!--json处理器--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <!--fastJson是一个JSON的处理工具包,由阿里巴巴公司研发推出。我们使用它将List或者Map转换成JSON对象--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.2.0</version> </dependency> <!--shiro安全依赖包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> </dependencies> <build> <!--项目打包时会将Java目录中的*.xml文件也进行打包--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>6.3.fastdfs-client配置文件## 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:221226.4.跨域配置GlobalCrosConfig.java/** * 跨域配置文件 **/ @Configuration public class GlobalCrosConfig 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小时。 时间单位是秒。 } } /* @Configuration public class GlobalCrosConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置 //.allowedOrigins("*") //开放哪些ip、端口、域名的访问权限 .allowedOriginPatterns("*") //开放哪些ip、端口、域名的访问权限 .allowCredentials(true) //是否允许发送Cookie信息 .allowedMethods("GET", "POST", "PUT", "DELETE") //开放哪些Http方法,允许跨域访问 .allowedHeaders("*") //允许HTTP请求中的携带哪些Header信息 .exposedHeaders("*"); //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息) } }; } }*/6.5.创建模型--实体类6.5.1.FastDfsFile.java/** * 文件类 */ @Data @NoArgsConstructor @AllArgsConstructor @Repository public class FastDfsFile { private Long id; private String fileId;//文件的fileId private String filePath;//文件路径 private Long fileSize;//文件大小 private String fileName;//文件名 private String ext;//文件的扩展名,不包含(.) private String fileType;//文件类型 private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //修改时间 }6.5.2.FastDfsFileType.java/** * 文件类型表 */ @Data @NoArgsConstructor @AllArgsConstructor @Repository public class FastDfsFileType { private Long id; private String fileType;//文件类型 private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //修改时间 }6.5.3.PageBean.java/** * 分页查询结果的封装类 */ @Data @NoArgsConstructor @AllArgsConstructor public class PageBean<T> { //当前页数 private int currPageNo; //每页显示的记录数 private int pageSize; //总记录数 private int totalCount; //总页数=总条数/每页显示的条数 private int totalPage; //每页的显示的数据 private List<T> lists; }6.5.4.R.java@Data @Accessors(chain = true) //对R进行链式操作 public class R { private Integer code;//响应码 private String message;//响应消息 private Map<String,Object> data=new HashMap<>(); public static R ok(){ R r = new R(); r.setCode(0); r.setMessage("成功"); return r; } public static R error(){ R r = new R(); r.setCode(-1); r.setMessage("失败"); return r; } public R data(String key,Object value){ this.data.put(key, value); return this; } }6.5.5.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); } }6.6.创建application.ymlserver: port: 9090 #Mybatis配置 mybatis: configuration: #sql日志 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #驼峰命名 map-underscore-to-camel-case: true mapper-locations: classpath:com/orange/fastdfs/mapper/*.xml #spring事务管理日志 logging: level: org.springframework.jdbc.support.JdbcTransactionManager: debug spring: #数据库连接信息 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.229.141:3306/fastdfs?characterEncoding=utf-8&&useSSL=false username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 # 文件上传的配置 # linux临时文件目录 # mkdir -p /data/tmp/updatefile servlet: multipart: location: /data/tmp/updatefile max-file-size: 10MB max-request-size: 10MB6.7.创建Service6.7.1.FastDfsFileServicepublic interface FastDfsFileService { void save(FastDfsFile fastDfsFile); void updateById(FastDfsFile fastDfsFile); FastDfsFile selectById(Long id); Long selectByFileId(String fileId); int deleteFastDfsFileById(Long id); List<FastDfsFile> selectAll(); PageBean<FastDfsFile> findFastDfsFileByPage(int currPageNo, int pageSize); }6.7.2.FastDfsFileServiceImpl@Service public class FastDfsFileServiceImpl implements FastDfsFileService { @Resource private FastDfsFileMapper fastDfsFileMapper; /** * 存储数据 * * @param fastDfsFile */ @Override public void save(FastDfsFile fastDfsFile) { fastDfsFileMapper.insert(fastDfsFile); } @Override public void updateById(FastDfsFile fastDfsFile) { fastDfsFileMapper.updateById(fastDfsFile); } @Override public FastDfsFile selectById(Long id) { return fastDfsFileMapper.selectById(id); } /** * 根据fileId查询id * * @param fileId * @return */ @Override public Long selectByFileId(String fileId) { return fastDfsFileMapper.selectByFileId(fileId); } /** * 根据id删除 * * @param id * @return */ @Override public int deleteFastDfsFileById(Long id) { return fastDfsFileMapper.deleteById(id); } /** * 查询所有 * * @return */ @Override public List<FastDfsFile> selectAll() { return fastDfsFileMapper.selectAll(); } @Override public PageBean<FastDfsFile> findFastDfsFileByPage(int currPageNo, int pageSize) { PageBean<FastDfsFile> pageBean = new PageBean<>(); //封装当前页数 pageBean.setCurrPageNo(currPageNo); //每页显示的条数 pageBean.setPageSize(pageSize); //总记录数 int totalCount = fastDfsFileMapper.selectCount(); pageBean.setTotalCount(totalCount); //总页数=总条数/每页显示的条数 Double num = Math.ceil(totalCount / pageSize); int totalPage = num.intValue(); pageBean.setTotalPage(totalPage); int offset = (currPageNo - 1) * pageSize; int limit = pageBean.getPageSize(); //封装每页显示的数据 List<FastDfsFile> list = fastDfsFileMapper.findeByPage(offset, limit); pageBean.setLists(list); return pageBean; } }6.7.3.FastDfsFileTypeServicepublic interface FastDfsFileTypeService { int selectByFileType(String fileType); }6.7.4.FastDfsFileTypeServiceImpl@Service public class FastDfsFileTypeServiceImpl implements FastDfsFileTypeService { @Resource private FastDfsFileTypeMapper fastDfsFileTypeMapper; @Override public int selectByFileType(String fileType) { return fastDfsFileTypeMapper.select(fileType); } }6.8.创建Mapper6.8.1.FastDfsFileMapper@Mapper public interface FastDfsFileMapper { void insert(FastDfsFile fastDfsFile); Long selectByFileId(String fileId); int deleteById(Long id); List<FastDfsFile> selectAll(); int selectCount(); List<FastDfsFile> findeByPage(int offset, int limit); void updateById(FastDfsFile fastDfsFile); FastDfsFile selectById(Long id); }6.8.2.FastDfsFileMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.orange.fastdfsjava.mapper.FastDfsFileMapper"> <insert id="insert" parameterType="com.orange.fastdfsjava.pojo.FastDfsFile"> insert into fast_dfs_file (file_id, file_path, file_size, ext, file_name, file_type, create_time, update_time) values (#{fileId}, #{filePath}, #{fileSize}, #{ext}, #{fileName}, #{fileType}, #{createTime}, #{updateTime}) </insert> <update id="updateById"> update fast_dfs_file set file_id = #{fileId}, file_path = #{filePath}, file_size = #{fileSize}, ext = #{ext}, file_name = #{fileName}, file_type = #{fileType}, create_time = #{createTime}, update_time = #{updateTime} where id = #{id} </update> <delete id="deleteById"> delete from fast_dfs_file where id = #{id} </delete> <select id="selectByFileId" resultType="java.lang.Long"> select id from fast_dfs_file where file_id = #{fileId} </select> <select id="selectAll" resultType="com.orange.fastdfsjava.pojo.FastDfsFile"> select * from fast_dfs_file </select> <select id="selectCount" resultType="java.lang.Integer"> select count(*) from fast_dfs_file </select> <select id="findeByPage" resultType="com.orange.fastdfsjava.pojo.FastDfsFile"> select * from fast_dfs_file limit #{offset},#{limit} </select> <select id="selectById" resultType="com.orange.fastdfsjava.pojo.FastDfsFile"> select * from fast_dfs_file where id = #{id} </select> </mapper>6.8.3.FastDfsFileTypeMapper@Mapper public interface FastDfsFileTypeMapper { int select(String fileType); }6.8.4.FastDfsFileTypeMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.orange.fastdfsjava.mapper.FastDfsFileTypeMapper"> <select id="select" resultType="java.lang.Integer"> select count(*) from fast_dfs_file_type where file_type = #{fileType} </select> </mapper>6.9.创建fastDFS客户端@Slf4j public class FastDFSClient { static { //加载fastDFS客户端的配置文件 try { ClientGlobal.initByProperties("config/fastdfs-client.properties"); log.info("network_timeout = {} ms", ClientGlobal.g_network_timeout); log.info("charset= {}", ClientGlobal.g_charset); } catch (IOException e) { e.printStackTrace(); } catch (MyException e) { e.printStackTrace(); } } /** * 上传文件 * @param file * @param fastDFSFile * @return * @throws IOException */ public static FastDfsFile upload(MultipartFile file, FastDfsFile fastDFSFile) throws IOException { 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(); //通过fastDSF的client代码访问tracker和storage try { //创建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", fastDFSFile.getFileName()); //执行上传 String fileId = storageClient.upload_file1(file_buff, fastDFSFile.getExt(), metaList); log.info("upload success. file id is: {}", fileId); fastDFSFile.setFileId(fileId); fastDFSFile.setFilePath(fileId); fastDFSFile.setFileSize(file.getSize()); fastDFSFile.setCreateTime(LocalDateTime.now()); fastDFSFile.setUpdateTime(LocalDateTime.now()); //通过调用service及dao将文件的路径存储到数据库中 //关闭storage客户端 storageClient.close(); return fastDFSFile; } catch (Exception e) { log.error("上传文件失败:", e); e.printStackTrace(); return null; } } }6.10.创建全局异常处理器/** * 全局异常处理器 */ @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) //捕获所有的异常 public Result ex(Exception ex){ ex.printStackTrace(); return Result.error("对不起,操作失败,请联系管理员"); } }6.11.创建Controller@Slf4j @RestController @RequestMapping("/fastDFSFile") public class FileServerController { @Resource private FastDfsFileService fastDfsFileService; @Resource private FastDfsFileTypeService fastDfsFileTypeService; @PostMapping("/upload") @ResponseBody public R upload(@RequestParam("file") MultipartFile file) throws IOException { //将文件先存储在web服务器上(本机),在调用fastDFS的client将文件上传到 fastDFS服务器 FastDfsFile fastDFSFile = new FastDfsFile(); String contentType = file.getContentType(); //检验当前文件是否在上述集合中 log.info("上传的文件类型为:{}", contentType); int count = fastDfsFileTypeService.selectByFileType(contentType); if (count < 1) { log.info("不支持此文件类型上传 : {}", contentType); return R.error().setCode(208).setMessage("不支持此文件类型上传 : " + contentType); } log.info("此文件类型为 : {}", contentType); fastDFSFile.setFileType(contentType); //文件原始名称 String originalFilename = file.getOriginalFilename(); log.info("原始文件名称 : {}", originalFilename); fastDFSFile.setFileName(originalFilename); //文件扩展名比如22.jpg String filenameExtension = StringUtils.getFilenameExtension(originalFilename); log.info("文件类型 = {}", filenameExtension);//jpg if (filenameExtension == null) { return R.error().setCode(208).setMessage("此文件没有文件扩展名"); } fastDFSFile.setExt(filenameExtension); //新文件名称 String fileName = UUID.randomUUID().toString().replace("-", "") + "." + filenameExtension; log.info("新文件名称 = {}", fileName); FastDfsFile fastDfsFile1 = FastDFSClient.upload(file, fastDFSFile); if (fastDfsFile1 != null) { fastDfsFileService.save(fastDfsFile1); Long id = fastDfsFileService.selectByFileId(fastDfsFile1.getFileId()); fastDfsFile1.setId(id); return R.ok().setCode(200).setMessage("上传成功").data("fastDfsFile",fastDfsFile1); } return R.error().setCode(208).setMessage("上传失败"); } @GetMapping("/getPageFastImg/{page}/{limit}") public R getPageFastImg(@PathVariable int page, @PathVariable int limit) { PageBean<FastDfsFile> pageBean = fastDfsFileService.findFastDfsFileByPage(page, limit); return R.ok().setCode(200).setMessage("查询成功").data("pageBean",pageBean); } }6.13.创建vue2项目6.14.安装相关模块npm install axios@1.5.0 npm install vue-axios npm install vue-router@3.0.1 npm i element-ui -S6.15.main.jsimport 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' // 设置全局变量 Vue.prototype.$baseImagePath = 'http://192.168.229.141' //把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), //将App组件放入容器中 data: { // 空的实例放到根组件下,所有的子组件都能调用 Bus: new Vue() } }).$mount('#app');6.16.src/router/index.js//在路由文件router/index.js中配置 // 该文件专门用于创建整个应用的路由器 import VueRouter from "vue-router"; //引入组件 import fastdfsuploadimg from '../view/fastdfs/fastdfsuploadimg'//上传页 import fastdfsimg from "../view/fastdfs/fastdfsimg" //创建路由 const routes = [ //定义路由 { path: '/', name: 'fastdfsuploadimg', component: fastdfsuploadimg }, { path: '/fastdfsimg', name: 'fastdfsimg', component: fastdfsimg }, ] //创建并暴露一个路由器 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router6.17.src/utils/request.jsimport axios from 'axios' // 创建axios实例 const service = axios.create({ baseURL: 'http://localhost:9090', // api 的 base_url timeout: 1000000 // 请求超时时间 }) // http request 拦截器 // 请求拦截器 service.interceptors.request.use( config => { // 可以在这里添加请求头等信息 return config; }, error => { // 请求错误处理 console.log(error); return Promise.reject(error); } ); // http response 拦截器 // 响应拦截器 service.interceptors.response.use( response => { // 对响应数据做处理,例如只返回data部分 const data = response.data; return data; }, error => { // 响应错误处理 console.log('err' + error); // for debug return Promise.reject(error); } ); export default service;6.18.src/api/fastdfs/fast.jsimport request from "../../utils/request"; const api_name = '/fastDFSFile' export default { //上传图片 uploadImg() { return request({ url: `${api_name}/upload`, method: 'post', }) }, getPageFastImg(page, limit) { return request({ url: `${api_name}/getPageFastImg/${page}/${limit}`, method: 'get', }) }, }6.19.上传图片页面fastdfsuploadimg.vue<template> <div> <h2>上传图片</h2> <el-form> <el-form-item> <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> <span v-if="dialogImageUrl != ''">图片地址: <a target="_blank" v-if="dialogImageUrl != ''" :href="dialogImageUrl">{{ dialogImageUrl }}</a> </span> <br/> </div> </template> import service from "../../utils/request"; <script> export default { name: "UploadImg", data() { return { dialogImageUrl: "", file_id: "", dialogVisible: false, //uploadUrl: "http://localhost:9090/fastDFSFile/upload", //文件上传地址 uploadUrl: "", //文件上传地址 datas: {}, }; }, methods: { onUploadSuccessIdCard(response) { this.file_id = response.data.fastDfsFile.fileId; this.datas = response.fastDfsFile.data; //this.dialogImageUrl = "http://192.168.229.141/" + response.data.fastDfsFile.filePath; this.dialogImageUrl = this.$baseImagePath+"/" + response.data.fastDfsFile.filePath; }, }, }; </script> <style scoped> </style>6.20.图片管理页面fastdfsimg.vue<template> <div> <h2>图片管理</h2> <!--图片列表--> <el-table size="small" style="margin: 30px;" empty-text="无数据" :data="imgList" highlight-current-row v-loading="loading" border element-loading-text="拼命加载中"> <el-table-column align="center" sortable prop="filePath" label="文件路径" width="450"></el-table-column> <el-table-column align="center" sortable prop="fileSize" label="文件大小" width="100"></el-table-column> <el-table-column align="center" sortable prop="fileName" label="文件名" width="130"></el-table-column> <el-table-column align="center" sortable prop="ext" label="扩展名" width="100"></el-table-column> <el-table-column align="center" sortable prop="fileType" label="文件类型" width="100"></el-table-column> <el-table-column align="center" sortable prop="filePath" label="预览图片" width="100"> <template slot-scope="scope"> <img :src="getImageUrl(scope.row.filePath)" style="max-width: 100px;max-height: 100px" alt="图标"/> </template> </el-table-column> </el-table> <!-- 分页 --> <el-pagination class="pagination" style="text-align: center;margin-top: 50px" layout="prev, pager, next" :current-page="page" :total="total" :page-size="limit"> </el-pagination> </div> </template> <script> import fastApi from "@/api/fastdfs/fast"; export default { name: "FastdfsImg", data() { return { total: 0, // 数据库中的总记录数 page: 1, // 默认页码 limit: 5, // 每页记录数 imgList: {}, //imagePath: 'http://192.168.229.141/', // 图片的基础路径 } }, created() { this.init() }, methods: { init() { fastApi.getPageFastImg(this.page, this.limit).then(response => { this.imgList = response.data.pageBean.lists this.total = response.data.pageBean.totalCount }) }, //获取图片路径 getImageUrl(filePath) { //return `${this.imagePath}${filePath}`; // 拼接图片路径 return this.$baseImagePath + '/' + filePath; // 拼接图片路径 }, }, } </script> <style scoped> </style>6.21.效果演示七、创建后台--分角色管理7.1.创建后台数据库表USE fastdfs; CREATE TABLE `admin` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '管理员表id', `role_id` int(11) DEFAULT '0' COMMENT '角色id', `role_name` varchar(50) DEFAULT NULL COMMENT '角色名', `name` varchar(100) NOT NULL COMMENT '管理员名', `password` varchar(50) NOT NULL COMMENT '管理员密码', `sex` varchar(50) DEFAULT NULL COMMENT '性别(男,女)', `age` int(11) DEFAULT NULL COMMENT '年龄', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态(0:未工作 1:工作中)', `email` varchar(50) DEFAULT NULL COMMENT '邮箱', `phone` varchar(11) DEFAULT NULL COMMENT '手机号', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='管理员表'; CREATE TABLE `role` ( `role_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色表id', `role_name` varchar(25) NOT NULL COMMENT '角色名\r\n1.医生\r\n2.科室\r\n3.医院管理员\r\n4.超级管理员', `description` varchar(255) DEFAULT NULL COMMENT '角色描述', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', `is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)', PRIMARY KEY (`role_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色表'; CREATE TABLE `menu` ( `menu_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '资源ID', `menu_name` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '资源名称', `parent_id` int(11) DEFAULT NULL COMMENT '父id', `url` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '资源路径', `menus` tinyint(4) DEFAULT '1' COMMENT '是否有子菜单(0:是,1:否)', `icon` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图标', `create_time` datetime NOT NULL COMMENT '创建日期', `update_time` datetime NOT NULL COMMENT '更新时间', `is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)', PRIMARY KEY (`menu_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=COMPACT COMMENT='菜单栏表'; CREATE TABLE `role_menu` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色菜单表id', `role_id` int(11) NOT NULL COMMENT '角色表id(外键)', `menu_id` int(11) NOT NULL COMMENT '菜单表id(外键)', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', `is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=137 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; INSERT INTO `admin` VALUES (1, 1, '超级管理员', 'root', '123456', '女', 18, 0, '123456@qq.com', '12345678902', '2021-06-30 12:14:13', '2024-02-14 21:40:26', 1); INSERT INTO `admin` VALUES (2, 2, '用户', 'admin', '123456', '男', 17, 0, '123456@qq.com', '12345678902', '2021-06-30 12:14:13', '2022-03-13 08:41:45', 1); INSERT INTO `menu` VALUES (2, '文件管理', 0, '', 1, 'el-icon-folder', '2021-06-27 19:58:39', '2024-02-14 22:20:52', 1); INSERT INTO `menu` VALUES (3, '我的文件', 2, 'file', 0, 'nel-icon-folder-opened', '2021-06-27 21:36:01', '2024-02-14 22:19:50', 1); INSERT INTO `menu` VALUES (4, '用户管理', 0, '', 1, 'el-icon-user', '2024-02-14 21:58:54', '2024-02-14 22:00:02', 1); INSERT INTO `menu` VALUES (5, '用户', 4, 'user', 0, 'el-icon-s-custom', '2021-06-27 21:37:17', '2024-02-14 22:15:37', 1); INSERT INTO `menu` VALUES (6, '个人信息管理', 0, NULL, 1, 'el-icon-user', '2022-03-04 17:12:46', '2024-02-14 21:59:26', 1); INSERT INTO `menu` VALUES (7, '我的信息', 6, 'info', 0, 'el-icon-user', '2022-03-04 17:13:23', '2024-02-14 22:00:27', 1); INSERT INTO `menu` VALUES (8, '修改邮箱', 6, 'email', 0, 'el-icon-user', '2022-03-05 10:53:37', '2024-02-14 22:00:29', 1); INSERT INTO `menu` VALUES (9, '修改密码', 6, 'password', 0, 'el-icon-user', '2022-03-05 10:54:53', '2024-02-14 22:00:33', 1); INSERT INTO `menu` VALUES (10, '文件上传', 2, 'upload', 0, 'el-icon-upload', '2024-02-14 22:18:49', '2024-02-14 22:18:49', 1); INSERT INTO `role` VALUES (1, '超级管理员', '拥有所有权限', '2022-03-04 14:46:19', '2022-03-04 15:30:00', 1); INSERT INTO `role` VALUES (2, '用户', '用户', '2022-03-04 14:46:19', '2024-02-14 21:53:44', 1); INSERT INTO `role_menu` VALUES (1, 2, 10, '2024-02-14 22:20:19', '2024-02-14 22:20:19', 1); INSERT INTO `role_menu` VALUES (2, 2, 2, '2024-02-14 22:02:28', '2024-02-14 22:14:49', 1); INSERT INTO `role_menu` VALUES (3, 2, 3, '2024-02-14 22:02:37', '2024-02-14 22:14:49', 1); INSERT INTO `role_menu` VALUES (4, 2, 6, '2024-02-14 22:03:05', '2024-02-14 22:14:49', 1); INSERT INTO `role_menu` VALUES (5, 2, 7, '2024-02-14 22:03:15', '2024-02-14 22:14:49', 1); INSERT INTO `role_menu` VALUES (6, 2, 8, '2024-02-14 22:03:23', '2024-02-14 22:14:49', 1); INSERT INTO `role_menu` VALUES (7, 2, 9, '2024-02-14 22:03:36', '2024-02-14 22:14:49', 1); INSERT INTO `role_menu` VALUES (9, 1, 4, '2024-02-14 22:04:10', '2024-02-14 22:14:55', 1); INSERT INTO `role_menu` VALUES (10, 1, 5, '2024-02-14 22:04:18', '2024-02-14 22:14:55', 1); INSERT INTO `role_menu` VALUES (11, 1, 6, '2024-02-14 22:04:25', '2024-02-14 22:14:55', 1); INSERT INTO `role_menu` VALUES (12, 1, 7, '2024-02-14 22:04:33', '2024-02-14 22:14:55', 1); INSERT INTO `role_menu` VALUES (13, 1, 8, '2024-02-14 22:04:44', '2024-02-14 22:14:55', 1); INSERT INTO `role_menu` VALUES (14, 1, 9, '2024-02-14 22:04:53', '2024-02-14 22:14:55', 1);7.2.创建实体类7.2.1.Admin/** * 管理员表 */ @Data @AllArgsConstructor @NoArgsConstructor @Repository public class Admin implements Serializable { private Integer id; //管理员表id private Integer roleId; //角色id private String roleName; //角色名 private String name; //管理员名 private String password; //管理员密码 private String sex; //性别(男,女) private Integer age; //年龄 private Integer status; //状态(0:未工作 1:工作中) private String email; //邮箱 private String phone; //手机号 private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //更新时间 private Integer isDeleted; //删除标记(0:不可用 1:可用) }7.2.2.Menu/** * 菜单栏表 */ @Data @AllArgsConstructor @NoArgsConstructor @Repository public class Menu implements Serializable { private Integer menuId; //资源ID private String menuName; //资源名称 private Integer parentId; //父id private String url; //资源路径 private Integer menus; //是否有子菜单(0:是,1:否) private String icon; //图标 private LocalDateTime createTime; //创建日期 private LocalDateTime updateTime; //更新时间 private Integer isDelete; //删除标记(0:不可用 1:可用) }7.2.3.MenuBean/** * 菜单栏表 */ @Data @AllArgsConstructor @NoArgsConstructor @Repository public class MenuBean implements Serializable { private Integer menuId; //资源ID private String menuName; //资源名称 private Integer parentId; //父id private String url; //资源路径 private Integer menus; //是否有子菜单(0:是,1:否) private String icon; //图标 private List<Menu> menuList; }7.2.4.Role/** * 角色表 */ @Data @AllArgsConstructor @NoArgsConstructor @Repository public class Role implements Serializable { private Integer roleId; //角色表id private String roleName; //角色名 1.用户 2.超级管理员 private String description; //角色描述 private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //更新时间 private Integer isDelete; //删除标记(0:不可用 1:可用) }7.2.5.RoleMenu/** * 角色菜单中间表 */ @Data @AllArgsConstructor @NoArgsConstructor @Repository public class RoleMenu implements Serializable { private Integer id; //角色菜单表id private Integer roleId; //角色表id(外键) private Integer menuId; //菜单表id(外键) private LocalDateTime createTime; //创建时间 private LocalDateTime updateTime; //更新时间 private Integer isDelete; //删除标记(0:不可用 1:可用) }7.3.编辑配置文件application.ymlserver: port: 9090 #Mybatis配置 mybatis: # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml # 如果是放在resource目录 classpath:/mapper/*Mapper.xml configuration: #sql日志 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #驼峰命名 map-underscore-to-camel-case: true mapper-locations: classpath:com/orange/fastdfs/mapper/*.xml #spring事务管理日志 logging: level: org.springframework.jdbc.support.JdbcTransactionManager: debug spring: application: name: fastdfs-java #应用的名字 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 #数据库连接信息 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.229.141:3306/fastdfs?characterEncoding=utf-8&&useSSL=false username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource #数据源其他配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 # 文件上传的配置 # linux临时文件目录 # mkdir -p /data/tmp/updatefile servlet: multipart: location: /data/tmp/updatefile max-file-size: 10MB max-request-size: 10MB7.4.编写工具类7.4.1.AuthContextHolder/** * 获取当前用户信息工具类 **/ public class AuthContextHolder { //获取当前用户id public static Integer getAdminId(HttpServletRequest request) { //从header获取token String token = request.getHeader("token"); //jwt从token获取userid Integer adminId = JwtHelper.getAdminId(token); return adminId; } //获取当前用户名称 public static String getAdminName(HttpServletRequest request) { //从header获取token String token = request.getHeader("token"); //jwt从token获取userid String adminName = JwtHelper.getAdminName(token); return adminName; } }7.4.2.HttpUtilspublic class HttpUtils { /** * 将通知参数转化为字符串 */ public static String readData(HttpServletRequest request) { BufferedReader br = null; try { StringBuilder result = new StringBuilder(); br = request.getReader(); for (String line; (line = br.readLine()) != null; ) { if (result.length() > 0) { result.append("\n"); } result.append(line); } return result.toString(); } catch (IOException e) { throw new RuntimeException(e); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } } }7.4.3.StringUtilspublic final class StringUtils { public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } public static boolean isBlank(CharSequence cs) { if (cs != null) { int length = cs.length(); for(int i = 0; i < length; ++i) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } } return true; } public static boolean isNotBlank(CharSequence cs) { return !isBlank(cs); } }7.4.4.JwtHelper/** * JWT token 工具类 **/ @Data @Component @ConfigurationProperties(prefix = "jwt") public class JwtHelper { //过期时间 private static long tokenExpiration; //签名秘钥 private static String tokenSignKey; //请求头 private static String tokenHeader; //校验token是否正确 public static boolean verify(String token,Integer adminId,String adminName) { try { String id = String.valueOf(adminId); Algorithm algorithm = Algorithm.HMAC256(id); JWTVerifier verifier = JWT.require(algorithm) .withClaim("adminName", adminName) .withClaim("adminId",adminId) .build(); DecodedJWT jwt = verifier.verify(token); return true; } catch (Exception e) { return false; } } /** * 生成签名 */ public static String createToken(Integer adminId,String adminName) { try { // 指定过期时间 Date date = new Date(System.currentTimeMillis() + tokenExpiration); String id = String.valueOf(adminId); Algorithm algorithm = Algorithm.HMAC256(id); return JWT.create() .withSubject("admin") .withClaim("adminName", adminName) .withClaim("adminId",adminId) .withExpiresAt(date) .sign(algorithm); } catch (UnsupportedEncodingException e) { return null; } } //根据token字符串得到用户id public static Integer getAdminId(String token) { if(StringUtils.isBlank(token)) { return null; } try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("adminId").asInt(); } catch (JWTDecodeException e) { return null; } } //根据token字符串得到用户名称 public static String getAdminName(String token) { if(StringUtils.isBlank(token)) { return ""; } try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("adminName").asString(); } catch (JWTDecodeException e) { return null; } } public static long getTokenExpiration() { return tokenExpiration; } public static void setTokenExpiration(long tokenExpiration) { JwtHelper.tokenExpiration = tokenExpiration; } public static String getTokenSignKey() { return tokenSignKey; } public static void setTokenSignKey(String tokenSignKey) { JwtHelper.tokenSignKey = tokenSignKey; } public static String getTokenHeader() { return tokenHeader; } public static void setTokenHeader(String tokenHeader) { JwtHelper.tokenHeader = tokenHeader; } public static void main(String[] args) { String token = JwtHelper.createToken(1,"小鱼儿"); System.out.println(token); System.out.println(JwtHelper.getAdminId(token)); System.out.println(JwtHelper.getAdminName(token)); boolean a = verify(token,1,"小鱼儿"); System.out.println(a); } }7.4.5.MD5/** * 1.MD5加密字符串(32位大写) * 2.MD5加密字符串(32位小写) * <p> * MD5在线加密:https://md5jiami.51240.com/ * 3.将二进制字节数组转换为十六进制字符串 * 4.Unicode中文编码转换成字符串 */ public class MD5 { /** * 生成md5 * @param message * @return */ public static String getMD5(String message) { String md5str = ""; try { //1 创建一个提供信息摘要算法的对象,初始化为md5算法对象 MessageDigest md = MessageDigest.getInstance("MD5"); //2 将消息变成byte数组 byte[] input = message.getBytes(); //3 计算后获得字节数组,这就是那128位了 byte[] buff = md.digest(input); //4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串 md5str = bytesToHex(buff); } catch (Exception e) { e.printStackTrace(); } return md5str; } /** * MD5加密字符串(32位大写) * * @param string 需要进行MD5加密的字符串 * @return 加密后的字符串(大写) */ public static String md5Encrypt32Upper(String string) { byte[] hash; try { //创建一个MD5算法对象,并获得MD5字节数组,16*8=128位 hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Huh, MD5 should be supported?", e); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Huh, UTF-8 should be supported?", e); } //转换为十六进制字符串 StringBuilder hex = new StringBuilder(hash.length * 2); for (byte b : hash) { if ((b & 0xFF) < 0x10) { hex.append("0"); } hex.append(Integer.toHexString(b & 0xFF)); } return hex.toString().toUpperCase(); } /** * MD5加密字符串(32位小写) * * @param string 需要进行MD5加密的字符串 * @return 加密后的字符串(小写) */ public static String md5Encrypt32Lower(String string) { byte[] hash; try { //创建一个MD5算法对象,并获得MD5字节数组,16*8=128位 hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Huh, MD5 should be supported?", e); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Huh, UTF-8 should be supported?", e); } //转换为十六进制字符串 StringBuilder hex = new StringBuilder(hash.length * 2); for (byte b : hash) { if ((b & 0xFF) < 0x10) { hex.append("0"); } hex.append(Integer.toHexString(b & 0xFF)); } return hex.toString().toLowerCase(); } /** * 将二进制字节数组转换为十六进制字符串 * * @param bytes 二进制字节数组 * @return 十六进制字符串 */ public static String bytesToHex(byte[] bytes) { StringBuffer md5str = new StringBuffer(); //把数组每一字节换成16进制连成md5字符串 int digital; for (int i = 0; i < bytes.length; i++) { digital = bytes[i]; if(digital < 0) { digital += 256; } if(digital < 16){ md5str.append("0"); } md5str.append(Integer.toHexString(digital)); } return md5str.toString().toUpperCase(); } /** * Unicode中文编码转换成字符串 */ public static String unicodeToString(String str) { Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))"); Matcher matcher = pattern.matcher(str); char ch; while (matcher.find()) { ch = (char) Integer.parseInt(matcher.group(2), 16); str = str.replace(matcher.group(1), ch + ""); } return str; } /* @Test public void testMD5(){ String message="123,123"; String result=getMD5(message); System.out.println(result); }*/ }7.4.6.ShiroMD5/** * MD5加密工具类 **/ @Slf4j public final class ShiroMD5 { /** * MD5对密码进行加密算法 */ public static String getPwd(String email,String password){ log.info("密码加盐,原密码为:"+password); Md5Hash md5 = new Md5Hash(password, email, 1024); String newMd5Password = md5.toHex(); System.out.println("加盐后的密码是:" +newMd5Password); log.info("密码加密后,生成的新密码为:"+newMd5Password); return newMd5Password; } }7.5.Mapper层7.5.1.AdminMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.orange.fastdfsjava.mapper.AdminMapper"> <select id="selectByNamePwd" resultType="com.orange.fastdfsjava.pojo.Admin"> select * from admin where name = #{name} and password = #{password} </select> <select id="selectById" resultType="com.orange.fastdfsjava.pojo.Admin"> select * from admin where id = #{id} </select> <select id="selectCountByNamePwd" resultType="java.lang.Integer"> select count(*) from admin where name = #{name} and password = #{password} </select> </mapper>7.5.2.MenuMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.orange.fastdfsjava.mapper.MenuMapper"> <select id="selectById" resultType="com.orange.fastdfsjava.pojo.Menu"> select * from menu where menu_id = #{menuId} </select> </mapper>7.5.3.RoleMenuMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.orange.fastdfsjava.mapper.RoleMenuMapper"> <select id="selectByRoleId" resultType="com.orange.fastdfsjava.pojo.RoleMenu"> select * from role_menu where role_id = #{roleId} </select> </mapper>7.5.4.AdminMapper@Mapper public interface AdminMapper { Admin selectByNamePwd(String name, String password); Admin selectById(Integer id); int selectCountByNamePwd(String name, String password); }7.5.5.MenuMapper@Mapper public interface MenuMapper { Menu selectById(Integer menuId); }7.5.6.RoleMenuMapper@Mapper public interface RoleMenuMapper { List<RoleMenu> selectByRoleId(Integer roleId); }7.6.Service层7.6.1.AdminServicepublic interface AdminService { Admin selectAdminLogin(String name, String password); Admin getAdminById(Integer adminId); int selectCountAdminLogin(String name, String password); }7.6.2.MenuBeanServicepublic interface MenuBeanService { List<MenuBean> selectByRoleId(Integer roleId); }7.6.3.MenuServicepublic interface MenuService { Menu selectByMenuId(Integer menuId); }7.6.4.RoleMenuServicepublic interface RoleMenuService { List<RoleMenu> selectByRoleId(Integer roleId); }7.6.5.AdminServiceImpl/** * 管理员表 */ @Service public class AdminServiceImpl implements AdminService { @Resource private AdminMapper adminMapper; @Override public Admin selectAdminLogin(String name, String password) { return adminMapper.selectByNamePwd(name, password); } @Override public Admin getAdminById(Integer id) { return adminMapper.selectById(id); } @Override public int selectCountAdminLogin(String name, String password) { return adminMapper.selectCountByNamePwd(name, password); } }7.6.6.MenuBeanServiceImpl@Service public class MenuBeanServiceImpl implements MenuBeanService { @Resource private RoleMenuMapper roleMenuMapper; @Resource private MenuMapper menuMapper; @Override public List<MenuBean> selectByRoleId(Integer roleId) { List<MenuBean> menuBeans = new ArrayList<>(); List<RoleMenu> roleMenuList = roleMenuMapper.selectByRoleId(roleId); for (RoleMenu roleMenu : roleMenuList){ Menu menu = menuMapper.selectById(roleMenu.getMenuId()); if (menu.getParentId()==0){ MenuBean menuBean = new MenuBean(); menuBean.setMenuId(menu.getMenuId()); menuBean.setMenuName(menu.getMenuName()); menuBean.setIcon(menu.getIcon()); menuBean.setUrl(menu.getUrl()); menuBean.setParentId(menu.getParentId()); menuBean.setMenus(menu.getMenus()); menuBeans.add(menuBean); } } List<MenuBean> menuBeanList = new ArrayList<>(); for (MenuBean menuBean : menuBeans){ MenuBean menuBean1 = menuBean; List<Menu> menuList = new ArrayList<>(); for (RoleMenu roleMenu : roleMenuList){ Menu menu1 = menuMapper.selectById(roleMenu.getMenuId()); if (menu1.getParentId().equals(menuBean.getMenuId())){ menuList.add(menu1); } } menuBean1.setMenuList(menuList); menuBeanList.add(menuBean1); } return menuBeanList; } }7.6.7.MenuServiceImpl/** * 菜单栏表 */ @Service public class MenuServiceImpl implements MenuService { @Resource private MenuMapper menuMapper; @Override public Menu selectByMenuId(Integer menuId) { return menuMapper.selectById(menuId); } }7.6.8.RoleMenuServiceImpl/** * 角色菜单中间表 */ @Service public class RoleMenuServiceImpl implements RoleMenuService { @Resource private RoleMenuMapper roleMenuMapper; @Override public List<RoleMenu> selectByRoleId(Integer roleId) { return roleMenuMapper.selectByRoleId(roleId); } }7.7.controller层7.7.1.AdminController/** * 管理员表 */ @Slf4j @CrossOrigin @RestController @RequestMapping("/admins") public class AdminController { @Resource private AdminService adminService; //管理员进行用户名密码登录 @PostMapping("{name}/{password}") public R loginAdmin(@PathVariable String name, @PathVariable String password) { log.info("管理员进行用户名:===>{},密码登录:===>{}", name, password); int count = adminService.selectCountAdminLogin(name, password); if (count > 0) { Admin admin = adminService.selectAdminLogin(name, password); String adminName = admin.getName(); String roleName = admin.getRoleName(); if (StringUtils.isNotBlank(adminName)) { String token = JwtHelper.createToken(admin.getId(), admin.getName()); return R.ok().setMessage("success").setCode(200).data("adminName", adminName).data("roleName", roleName).data("token", token); } } return R.error().setMessage("fail"); } //根据id查询管理员 @GetMapping() public R getAdminById(HttpServletRequest request) { Integer adminId = AuthContextHolder.getAdminId(request); Admin admin = adminService.getAdminById(adminId); log.info("根据id查询管理员:===>{}", admin); return R.ok().setMessage("success").setCode(200).data("admin", admin); } }7.7.2.MenuController/** * 菜单栏表 前端控制器 */ @Slf4j @RestController @RequestMapping("/menus") public class MenuController { @Resource private AdminService adminService; @Resource private MenuBeanService menuBeanService; /** * 根据角色获取不同的菜单栏列表 * @param request * @return * @throws Exception */ @GetMapping() public R getMenuByRoleId(HttpServletRequest request) throws Exception{ try { int adminId = AuthContextHolder.getAdminId(request); Admin admin = adminService.getAdminById(adminId); Integer roleId = admin.getRoleId(); List<MenuBean> menuBeanList = menuBeanService.selectByRoleId(roleId); return R.ok().setCode(200).setMessage("查询菜单列表success").data("menuBeanList",menuBeanList); }catch (Exception e){ return R.error().setCode(208).setMessage("请求失败,请进行登录后操作"); } } }7.8.前端vue改造npm install js-cookie --save npm install --save vue-qriously npm install --save vuex7.8.1.登录login.vue<template> <div class="login-wrap"> <el-form label-position="left" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="demo-ruleForm login-container"> <h3 class="title">用户登录</h3> <el-form-item prop="username"> <el-input type="text" v-model="ruleForm.username" auto-complete="off" placeholder="账号"></el-input> </el-form-item> <el-form-item prop="password"> <el-input type="password" v-model="ruleForm.password" auto-complete="off" placeholder="密码"></el-input> </el-form-item> <el-row> <el-col :span="12"> <el-form-item prop="code"> <el-input type="text" v-model="ruleForm.code" auto-complete="off" placeholder="图形验证码" @keyup.enter.native="submitForm('ruleForm')"></el-input> </el-form-item> </el-col> <el-col :span="12" class="code-box"> <img :src="ruleForm.codeimg" alt="" class="codeimg"> <div @click="refreshCode" style="margin-right: 50px;margin-top: -18px"> <SIdentify :identifyCode="identifyCode"></SIdentify> </div> </el-col> </el-row> <el-checkbox class="remember" v-model="rememberpwd">记住密码</el-checkbox> <el-form-item style="width:100%;"> <el-button type="primary" style="width:100%;" @click="submitForm('ruleForm')" :loading="logining">登录</el-button> </el-form-item> </el-form> </div> </template> <script type="text/ecmascript-6"> import cookie from "js-cookie"; import SIdentify from "@/view/code/identify"; import adminApi from "../api/fastdfs/admin"; export default { name: 'login', components: { SIdentify }, data() { return { //定义loading默认为false logining: false, // 记住密码 rememberpwd: false, ruleForm: { //username和password默认为空 username: '', password: '', code: '', randomStr: '', codeimg: '' }, //rules前端验证 rules: { username: [{ required: true, message: '请输入账号', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }], code: [{ required: true, message: '请输入验证码', trigger: 'blur' }] }, //验证码 identifyCodes: "1234567890", identifyCode: "" } }, // 创建完毕状态(里面是操作) created() { this.$message({ message: '账号密码及验证码不为空即可', type: 'success' }) }, mounted() { const self = this self.identifyCode = ""; self.makeCode(this.identifyCodes, 4); self.ruleForm.codeimg = self.identifyCode //验证码输入框 self.ruleForm.code = self.identifyCode }, // 里面的函数只有调用才会执行 methods: { //生成验证码工具 randomNum(min, max) { return Math.floor(Math.random() * (max - min) + min); }, //刷新验证码 refreshCode() { this.identifyCode = ""; this.makeCode(this.identifyCodes, 4); this.ruleForm.codeimg = this.identifyCode //验证码输入框 this.ruleForm.code = this.identifyCode }, //验证 makeCode(o, l) { for (let i = 0; i < l; i++) { this.identifyCode += this.identifyCodes[ this.randomNum(0, this.identifyCodes.length) ]; } }, setCookies(adminName, token,roleName) { cookie.set('adminToken', token, { domain: 'localhost' }) cookie.set('adminName', adminName, { domain: 'localhost' }) cookie.set('roleName', roleName, { domain: 'localhost' }) }, //获取info列表 submitForm(formName) { this.$refs[formName].validate(valid => { if (valid) { if (this.ruleForm.code===this.ruleForm.codeimg){ adminApi.loginAdmin(this.ruleForm.username,this.ruleForm.password).then(response=>{ if (response.code == 200){ this.$message.success("登录成功") // 登录成功 设置cookie this.setCookies(response.data.adminName,response.data.token,response.data.roleName) this.$router.push('main') }else { this.$message.error("用户名或者密码错误...") } }) }else { // 获取图形验证码 this.$message.error('验证码输入密码!') } } else { this.logining = false return false } }) }, } } </script> <style scoped> .login-wrap { box-sizing: border-box; width: 100%; height: 100%; padding-top: 10%; background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjEzNjFweCIgaGVpZ2h0PSI2MDlweCIgdmlld0JveD0iMCAwIDEzNjEgNjA5IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA0Ni4yICg0NDQ5NikgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+R3JvdXAgMjE8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iQW50LURlc2lnbi1Qcm8tMy4wIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0i6LSm5oi35a+G56CB55m75b2VLeagoemqjCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTc5LjAwMDAwMCwgLTgyLjAwMDAwMCkiPgogICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMjEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDc3LjAwMDAwMCwgNzMuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMTgiIG9wYWNpdHk9IjAuOCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzQuOTAxNDE2LCA1NjkuNjk5MTU4KSByb3RhdGUoLTcuMDAwMDAwKSB0cmFuc2xhdGUoLTc0LjkwMTQxNiwgLTU2OS42OTkxNTgpIHRyYW5zbGF0ZSg0LjkwMTQxNiwgNTI1LjE5OTE1OCkiPgogICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLTExIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjI1IiBjeD0iNjMuNTc0ODc5MiIgY3k9IjMyLjQ2ODM2NyIgcng9IjIxLjc4MzA0NzkiIHJ5PSIyMS43NjYwMDgiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0zIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjU5OTk5OTk2NCIgY3g9IjUuOTg3NDY0NzkiIGN5PSIxMy44NjY4NjAxIiByeD0iNS4yMTczOTEzIiByeT0iNS4yMTMzMDk5NyI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zOC4xMzU0NTE0LDg4LjM1MjAyMTUgQzQzLjg5ODQyMjcsODguMzUyMDIxNSA0OC41NzAyMzQsODMuNjgzODY0NyA0OC41NzAyMzQsNzcuOTI1NDAxNSBDNDguNTcwMjM0LDcyLjE2NjkzODMgNDMuODk4NDIyNyw2Ny40OTg3ODE2IDM4LjEzNTQ1MTQsNjcuNDk4NzgxNiBDMzIuMzcyNDgwMSw2Ny40OTg3ODE2IDI3LjcwMDY2ODgsNzIuMTY2OTM4MyAyNy43MDA2Njg4LDc3LjkyNTQwMTUgQzI3LjcwMDY2ODgsODMuNjgzODY0NyAzMi4zNzI0ODAxLDg4LjM1MjAyMTUgMzguMTM1NDUxNCw4OC4zNTIwMjE1IFoiIGlkPSJPdmFsLTMtQ29weSIgZmlsbD0iI0NGREFFNiIgb3BhY2l0eT0iMC40NSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02NC4yNzc1NTgyLDMzLjE3MDQ5NjMgTDExOS4xODU4MzYsMTYuNTY1NDkxNSIgaWQ9IlBhdGgtMTIiIHN0cm9rZT0iI0NGREFFNiIgc3Ryb2tlLXdpZHRoPSIxLjczOTEzMDQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNDIuMTQzMTcwOCwyNi41MDAyNjgxIEw3LjcxMTkwMTYyLDE0LjU2NDA3MDIiIGlkPSJQYXRoLTE2IiBzdHJva2U9IiNFMEI0QjciIHN0cm9rZS13aWR0aD0iMC43MDI2Nzg5NjQiIG9wYWNpdHk9IjAuNyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtZGFzaGFycmF5PSIxLjQwNTM1Nzg5OTg3MzE1MywyLjEwODAzNjk1MzQ2OTk4MSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02My45MjYyMTg3LDMzLjUyMTU2MSBMNDMuNjcyMTMyNiw2OS4zMjUwOTUxIiBpZD0iUGF0aC0xNSIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjAuNzAyNjc4OTY0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1kYXNoYXJyYXk9IjEuNDA1MzU3ODk5ODczMTUzLDIuMTA4MDM2OTUzNDY5OTgxIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjYuODUwOTIyLCAxMy41NDM2NTQpIHJvdGF0ZSgzMC4wMDAwMDApIHRyYW5zbGF0ZSgtMTI2Ljg1MDkyMiwgLTEzLjU0MzY1NCkgdHJhbnNsYXRlKDExNy4yODU3MDUsIDQuMzgxODg5KSIgZmlsbD0iI0NGREFFNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLTQiIG9wYWNpdHk9IjAuNDUiIGN4PSI5LjEzNDgyNjUzIiBjeT0iOS4xMjc2ODA3NiIgcng9IjkuMTM0ODI2NTMiIHJ5PSI5LjEyNzY4MDc2Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xOC4yNjk2NTMxLDE4LjI1NTM2MTUgQzE4LjI2OTY1MzEsMTMuMjE0MjgyNiAxNC4xNzk4NTE5LDkuMTI3NjgwNzYgOS4xMzQ4MjY1Myw5LjEyNzY4MDc2IEM0LjA4OTgwMTE0LDkuMTI3NjgwNzYgMCwxMy4yMTQyODI2IDAsMTguMjU1MzYxNSBMMTguMjY5NjUzMSwxOC4yNTUzNjE1IFoiIGlkPSJPdmFsLTQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkuMTM0ODI3LCAxMy42OTE1MjEpIHNjYWxlKC0xLCAtMSkgdHJhbnNsYXRlKC05LjEzNDgyNywgLTEzLjY5MTUyMSkgIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTYuMjk0NzAwLCAxMjMuNzI1NjAwKSByb3RhdGUoLTUuMDAwMDAwKSB0cmFuc2xhdGUoLTIxNi4yOTQ3MDAsIC0xMjMuNzI1NjAwKSB0cmFuc2xhdGUoMTA2LjI5NDcwMCwgMzUuMjI1NjAwKSI+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMiIgZmlsbD0iI0NGREFFNiIgb3BhY2l0eT0iMC4yNSIgY3g9IjI5LjExNzY0NzEiIGN5PSIyOS4xNDAyNDM5IiByeD0iMjkuMTE3NjQ3MSIgcnk9IjI5LjE0MDI0MzkiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yIiBmaWxsPSIjQ0ZEQUU2IiBvcGFjaXR5PSIwLjMiIGN4PSIyOS4xMTc2NDcxIiBjeT0iMjkuMTQwMjQzOSIgcng9IjIxLjU2ODYyNzUiIHJ5PSIyMS41ODUzNjU5Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMi1Db3B5IiBzdHJva2U9IiNDRkRBRTYiIG9wYWNpdHk9IjAuNCIgY3g9IjE3OS4wMTk2MDgiIGN5PSIxMzguMTQ2MzQxIiByeD0iMjMuNzI1NDkwMiIgcnk9IjIzLjc0MzkwMjQiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yIiBmaWxsPSIjQkFDQUQ5IiBvcGFjaXR5PSIwLjUiIGN4PSIyOS4xMTc2NDcxIiBjeT0iMjkuMTQwMjQzOSIgcng9IjEwLjc4NDMxMzciIHJ5PSIxMC43OTI2ODI5Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI5LjExNzY0NzEsMzkuOTMyOTI2OCBMMjkuMTE3NjQ3MSwxOC4zNDc1NjEgQzIzLjE2MTYzNTEsMTguMzQ3NTYxIDE4LjMzMzMzMzMsMjMuMTc5NjA5NyAxOC4zMzMzMzMzLDI5LjE0MDI0MzkgQzE4LjMzMzMzMzMsMzUuMTAwODc4MSAyMy4xNjE2MzUxLDM5LjkzMjkyNjggMjkuMTE3NjQ3MSwzOS45MzI5MjY4IFoiIGlkPSJPdmFsLTIiIGZpbGw9IiNCQUNBRDkiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtOSIgb3BhY2l0eT0iMC40NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTcyLjAwMDAwMCwgMTMxLjAwMDAwMCkiIGZpbGw9IiNFNkExQTYiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0yLUNvcHktMiIgY3g9IjcuMDE5NjA3ODQiIGN5PSI3LjE0NjM0MTQ2IiByeD0iNi40NzA1ODgyNCIgcnk9IjYuNDc1NjA5NzYiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTAuNTQ5MDE5NjA4LDEzLjYyMTk1MTIgQzQuMTIyNjI2ODEsMTMuNjIxOTUxMiA3LjAxOTYwNzg0LDEwLjcyMjcyMiA3LjAxOTYwNzg0LDcuMTQ2MzQxNDYgQzcuMDE5NjA3ODQsMy41Njk5NjA5NSA0LjEyMjYyNjgxLDAuNjcwNzMxNzA3IDAuNTQ5MDE5NjA4LDAuNjcwNzMxNzA3IEwwLjU0OTAxOTYwOCwxMy42MjE5NTEyIFoiIGlkPSJPdmFsLTItQ29weS0yIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzLjc4NDMxNCwgNy4xNDYzNDEpIHNjYWxlKC0xLCAxKSB0cmFuc2xhdGUoLTMuNzg0MzE0LCAtNy4xNDYzNDEpICI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0xMCIgZmlsbD0iI0NGREFFNiIgY3g9IjIxOC4zODIzNTMiIGN5PSIxMzguNjg1OTc2IiByeD0iMS42MTc2NDcwNiIgcnk9IjEuNjE4OTAyNDQiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC0xMC1Db3B5LTIiIGZpbGw9IiNFMEI0QjciIG9wYWNpdHk9IjAuMzUiIGN4PSIxNzkuNTU4ODI0IiBjeT0iMTc1LjM4MTA5OCIgcng9IjEuNjE3NjQ3MDYiIHJ5PSIxLjYxODkwMjQ0Ij48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtMTAtQ29weSIgZmlsbD0iI0UwQjRCNyIgb3BhY2l0eT0iMC4zNSIgY3g9IjE4MC4wOTgwMzkiIGN5PSIxMDIuNTMwNDg4IiByeD0iMi4xNTY4NjI3NSIgcnk9IjIuMTU4NTM2NTkiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMjguOTk4NTM4MSwyOS45NjcxNTk4IEwxNzEuMTUxMDE4LDEzMi44NzYwMjQiIGlkPSJQYXRoLTExIiBzdHJva2U9IiNDRkRBRTYiIG9wYWNpdHk9IjAuOCI+PC9wYXRoPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTEwIiBvcGFjaXR5PSIwLjc5OTk5OTk1MiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA1NC4xMDA2MzUsIDM2LjY1OTMxNykgcm90YXRlKC0xMS4wMDAwMDApIHRyYW5zbGF0ZSgtMTA1NC4xMDA2MzUsIC0zNi42NTkzMTcpIHRyYW5zbGF0ZSgxMDI2LjYwMDYzNSwgNC42NTkzMTcpIj4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbC03IiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiIGN4PSI0My44MTM1NTkzIiBjeT0iMzIiIHJ4PSIxMS4xODY0NDA3IiByeT0iMTEuMjk0MTE3NiI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzQuNTk2Nzc0LCAyMy4xMTExMTEpIiBmaWxsPSIjQkFDQUQ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsbGlwc2UgaWQ9Ik92YWwtNyIgb3BhY2l0eT0iMC40NSIgY3g9IjkuMTg1MzQ3MTgiIGN5PSI4Ljg4ODg4ODg5IiByeD0iOC40NzQ1NzYyNyIgcnk9IjguNTU2MTQ5NzMiPjwvZWxsaXBzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkuMTg1MzQ3MTgsMTcuNDQ1MDM4NiBDMTMuODY1NzI2NCwxNy40NDUwMzg2IDE3LjY1OTkyMzUsMTMuNjE0MzE5OSAxNy42NTk5MjM1LDguODg4ODg4ODkgQzE3LjY1OTkyMzUsNC4xNjM0NTc4NyAxMy44NjU3MjY0LDAuMzMyNzM5MTU2IDkuMTg1MzQ3MTgsMC4zMzI3MzkxNTYgTDkuMTg1MzQ3MTgsMTcuNDQ1MDM4NiBaIiBpZD0iT3ZhbC03Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zNC42NTk3Mzg1LDI0LjgwOTY5NCBMNS43MTY2NjA4NCw0Ljc2ODc4OTQ1IiBpZD0iUGF0aC0yIiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8ZWxsaXBzZSBpZD0iT3ZhbCIgc3Ryb2tlPSIjQ0ZEQUU2IiBzdHJva2Utd2lkdGg9IjAuOTQxMTc2NDcxIiBjeD0iMy4yNjI3MTE4NiIgY3k9IjMuMjk0MTE3NjUiIHJ4PSIzLjI2MjcxMTg2IiByeT0iMy4yOTQxMTc2NSI+PC9lbGxpcHNlPgogICAgICAgICAgICAgICAgICAgIDxlbGxpcHNlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGN0UxQUQiIGN4PSIyLjc5NjYxMDE3IiBjeT0iNjEuMTc2NDcwNiIgcng9IjIuNzk2NjEwMTciIHJ5PSIyLjgyMzUyOTQxIj48L2VsbGlwc2U+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTM0LjYzMTI0NDMsMzkuMjkyMjcxMiBMNS4wNjM2NjY2Myw1OS43ODUwODIiIGlkPSJQYXRoLTEwIiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMC45NDExNzY0NzEiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xOSIgb3BhY2l0eT0iMC4zMyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI4Mi41MzcyMTksIDQ0Ni41MDI4NjcpIHJvdGF0ZSgtMTAuMDAwMDAwKSB0cmFuc2xhdGUoLTEyODIuNTM3MjE5LCAtNDQ2LjUwMjg2NykgdHJhbnNsYXRlKDExNDIuNTM3MjE5LCAzMjcuNTAyODY3KSI+CiAgICAgICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTE3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDEuMzMzNTM5LCAxMDQuNTAyNzQyKSByb3RhdGUoMjc1LjAwMDAwMCkgdHJhbnNsYXRlKC0xNDEuMzMzNTM5LCAtMTA0LjUwMjc0MikgdHJhbnNsYXRlKDEyOS4zMzM1MzksIDkyLjUwMjc0MikiIGZpbGw9IiNCQUNBRDkiPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLTQiIG9wYWNpdHk9IjAuNDUiIGN4PSIxMS42NjY2NjY3IiBjeT0iMTEuNjY2NjY2NyIgcj0iMTEuNjY2NjY2NyI+PC9jaXJjbGU+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMy4zMzMzMzMzLDIzLjMzMzMzMzMgQzIzLjMzMzMzMzMsMTYuODkwMDExMyAxOC4xMDk5ODg3LDExLjY2NjY2NjcgMTEuNjY2NjY2NywxMS42NjY2NjY3IEM1LjIyMzM0NDU5LDExLjY2NjY2NjcgMCwxNi44OTAwMTEzIDAsMjMuMzMzMzMzMyBMMjMuMzMzMzMzMywyMy4zMzMzMzMzIFoiIGlkPSJPdmFsLTQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExLjY2NjY2NywgMTcuNTAwMDAwKSBzY2FsZSgtMSwgLTEpIHRyYW5zbGF0ZSgtMTEuNjY2NjY3LCAtMTcuNTAwMDAwKSAiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC01LUNvcHktNiIgZmlsbD0iI0NGREFFNiIgY3g9IjIwMS44MzMzMzMiIGN5PSI4Ny41IiByPSI1LjgzMzMzMzMzIj48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQzLjUsODguODEyNjY4NSBMMTU1LjA3MDUwMSwxNy42MDM4NTQ0IiBpZD0iUGF0aC0xNyIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTcuNSwzNy4zMzMzMzMzIEwxMjcuNDY2MjUyLDk3LjY0NDk3MzUiIGlkPSJQYXRoLTE4IiBzdHJva2U9IiNCQUNBRDkiIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwb2x5bGluZSBpZD0iUGF0aC0xOSIgc3Ryb2tlPSIjQ0ZEQUU2IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciIHBvaW50cz0iMTQzLjkwMjU5NyAxMjAuMzAyMjgxIDE3NC45MzU0NTUgMjMxLjU3MTM0MiAzOC41IDE0Ny41MTA4NDcgMTI2LjM2Njk0MSAxMTAuODMzMzMzIj48L3BvbHlsaW5lPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTkuODMzMzMzLDk5Ljc0NTM4NDIgTDE5NS40MTY2NjcsODkuMjUiIGlkPSJQYXRoLTIwIiBzdHJva2U9IiNFMEI0QjciIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyIgb3BhY2l0eT0iMC42Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIwNS4zMzMzMzMsODIuMTM3MjEwNSBMMjM4LjcxOTQwNiwzNi4xNjY2NjY3IiBpZD0iUGF0aC0yNCIgc3Ryb2tlPSIjQkFDQUQ5IiBzdHJva2Utd2lkdGg9IjEuMTY2NjY2NjciPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMjY2LjcyMzQyNCwxMzIuMjMxOTg4IEwyMDcuMDgzMzMzLDkwLjQxNjY2NjciIGlkPSJQYXRoLTI1IiBzdHJva2U9IiNDRkRBRTYiIHN0cm9rZS13aWR0aD0iMS4xNjY2NjY2NyI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNSIgZmlsbD0iI0MxRDFFMCIgY3g9IjE1Ni45MTY2NjciIGN5PSI4Ljc1IiByPSI4Ljc1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLTUtQ29weS0zIiBmaWxsPSIjQzFEMUUwIiBjeD0iMzkuMDgzMzMzMyIgY3k9IjE0OC43NSIgcj0iNS4yNSI+PC9jaXJjbGU+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC01LUNvcHktMiIgZmlsbC1vcGFjaXR5PSIwLjYiIGZpbGw9IiNEMURFRUQiIGN4PSI4Ljc1IiBjeT0iMzMuMjUiIHI9IjguNzUiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNS1Db3B5LTQiIGZpbGwtb3BhY2l0eT0iMC42IiBmaWxsPSIjRDFERUVEIiBjeD0iMjQzLjgzMzMzMyIgY3k9IjMwLjMzMzMzMzMiIHI9IjUuODMzMzMzMzMiPjwvY2lyY2xlPgogICAgICAgICAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwtNS1Db3B5LTUiIGZpbGw9IiNFMEI0QjciIGN4PSIxNzUuNTgzMzMzIiBjeT0iMjMyLjc1IiByPSI1LjI1Ij48L2NpcmNsZT4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+); /* background-color: #112346; */ background-repeat: no-repeat; background-position: center right; background-size: 100%; } .login-container { border-radius: 10px; margin: 0px auto; width: 350px; padding: 30px 35px 15px 35px; background: #fff; border: 1px solid #eaeaea; text-align: left; box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.1); } .title { margin: 0px auto 40px auto; text-align: center; color: #505458; } .remember { margin: 0px 0px 35px 0px; } .code-box { text-align: right; } .codeimg { height: 40px; } </style> 7.8.2.后台页面admin.vue<template> <el-container class="index-con"> <el-aside :class="showclass"> <leftnav></leftnav> </el-aside> <el-container class="main-con"> <el-header class="index-header"> <navcon></navcon> </el-header> <el-main clss="index-main"> <router-view> </router-view> </el-main> </el-container> </el-container> </template> <script> // 导入组件 import navcon from '../components/navcon.vue' import leftnav from '../components/leftnav.vue' export default { name: 'admin', data() { return { showclass: 'asideshow', showtype: false } }, // 注册组件 components: { navcon, leftnav }, methods: {}, created() { // 监听 this.$root.Bus.$on('toggle', value => { if (value) { this.showclass = 'asideshow' } else { setTimeout(() => { this.showclass = 'aside' }, 300) } }) }, beforeUpdate() {}, // 挂载前状态(里面是操作) beforeMount() { // 弹出登录成功 /*this.$message({ message: '登录成功', type: 'success' })*/ } } </script> <style > .index-con { height: 100%; width: 100%; box-sizing: border-box; } .aside { width: 64px !important; height: 100%; background-color: #334157; margin: 0px; } .asideshow { width: 240px !important; height: 100%; background-color: #334157; margin: 0px; } .index-header, .index-main { padding: 0px; border-left: 2px solid #333; } </style> 7.8.2.后台顶部页面navcon.vue/** * 头部菜单 */ <template> <el-menu class="el-menu-demo" mode="horizontal" background-color="#334157" text-color="#fff" active-text-color="#fff"> <el-button class="buttonimg"> <img class="showimg" :src="collapsed?imgsq:imgshow" @click="toggle(collapsed)"> </el-button> <el-submenu index="2" class="submenu"> <template slot="title">{{adminName}}</template> <el-menu-item>设置</el-menu-item> <el-menu-item>个人中心</el-menu-item> <el-menu-item @click="exit()" index="2-3">退出</el-menu-item> </el-submenu> </el-menu> </template> <script> import cookie from "js-cookie"; export default { name: 'navcon', data() { return { collapsed: true, imgshow: require('../https://www.hwpdyl.com/assets/img/show.png'), imgsq: require('../https://www.hwpdyl.com/assets/img/sq.png'), adminName: '' } }, // 创建完毕状态(里面是操作) created() { this.adminName = cookie.get('adminName') }, methods: { // 退出登录 exit() { this.$confirm('退出登录, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { setTimeout(() => { this.$router.push({ path: '/' }) cookie.set('adminToken', '', {domain: 'localhost'}) cookie.set('adminName', '', {domain: 'localhost'}) this.$message({ type: 'success', message: '已退出登录!' }) }, 1000) }) .catch(() => { this.$message({ type: 'info', message: '已取消' }) }) }, // 切换显示 toggle(showtype) { this.collapsed = !showtype this.$root.Bus.$emit('toggle', this.collapsed) } } } </script> <style scoped> .el-menu-vertical-demo:not(.el-menu--collapse) { border: none; } .submenu { float: right; position: absolute; right: 0; top: 0px; } .buttonimg { height: 60px; background-color: transparent; border: none; } .showimg { width: 26px; height: 26px; position: absolute; top: 17px; left: 17px; } .showimg:active { border: none; } </style> 7.8.2.后台左侧页面leftnav.vue/** * 左边菜单 */ <template> <el-menu :collapse="collapsed" collapse-transition router unique-opened class="el-menu-vertical-demo" background-color="#334157" text-color="#fff" active-text-color="#ffd04b"> <div class="logobox"> <img class="logoimg" src="../https://www.hwpdyl.com/assets/img/logo.png" alt=""> </div> <router-link to="/main"> <el-menu-item index="Index"> <template slot="title"> <i class="el-icon-s-home"></i> <span slot="title">首页</span> </template> </el-menu-item> </router-link> <el-submenu v-for="menu in menuBeanList" :key="menu.menuid" :index="menu.menuName"> <template slot="title"> <i class="iconfont" :class="menu.icon"></i> <span>{{menu.menuName}}</span> </template> <el-menu-item-group v-if="menu.menuList != null"> <el-menu-item v-for="chmenu in menu.menuList" :index="'/'+chmenu.url" :key="chmenu.menuId"> <i class="iconfont" :class="chmenu.icon"></i> <span>{{chmenu.menuName}}</span> </el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </template> <script> import menuApi from "../api/fastdfs/menu"; export default { name: 'leftnav', data() { return { collapsed: false, allmenu: [], menuBeanList:[], } }, // 创建完毕状态(里面是操作) created() { this.$root.Bus.$on('toggle', value => { this.collapsed = !value }) menuApi.getMenuByRoleId().then(response=>{ this.menuBeanList = response.data.menuBeanList }) } } </script> <style> .el-menu-vertical-demo:not(.el-menu--collapse) { width: 240px; min-height: 400px; } .el-menu-vertical-demo:not(.el-menu--collapse) { border: none; text-align: left; } .el-menu-item-group__title { padding: 0px; } .el-menu-bg { background-color: #1f2d3d !important; } .el-menu { border: none; } .logobox { height: 40px; line-height: 40px; color: #9d9d9d; font-size: 20px; text-align: center; padding: 20px 0px; } .logoimg { height: 40px; } </style> 7.8.2.主界面main.vue<template> <div> <div class="body" style="z-index:1"> <div class="stars" ref="starsRef"> <div class="star" v-for="(item, index) in starsCount" :key="index"></div> <div style="z-index: 10;font-size: 50px;color: #a0cfff"> <span>欢迎登录后台管理系统=>{{ adminName }}</span> </div> </div> <div style="z-index: 10;font-size: 50px;color: #a0cfff;margin-top: 200px;margin-left: 200px"> <span>欢迎登录后台管理系统=>{{ adminName }}</span> </div> </div> </div> </template> <script> import cookie from "js-cookie"; export default { name: "main", data() { return { adminName: '', num: 5, starsCount: 1000, //星星数量 distance: 900, //间距 } }, // 创建完毕状态(里面是操作) created() { this.adminName = cookie.get('adminName') }, mounted() { console.log(this.$refs.starsRef.children); let starNodes = Array.from(this.$refs.starsRef.children); starNodes.forEach((item) => { let speed = 0.2 + Math.random() * 1; let thisDistance = this.distance + Math.random() * 300; item.style.transformOrigin = `0 0 ${thisDistance}px`; item.style.transform = `translate3d(0,0,-${thisDistance}px) rotateY(${Math.random() * 360}deg) rotateX(${Math.random() * -50}deg) scale(${speed},${speed})`; }); } } </script> <style scoped> .body { position: absolute; width: 86%; height: 90%; margin: 0; padding: 0; background: radial-gradient(200% 100% at bottom center, #f7f7b6, #e96f92, #1b2947); background: radial-gradient(200% 105% at top center, #1b2947 10%, #75517d 40%, #e96f92 65%, #f7f7b6); background-attachment: fixed; overflow: hidden; } @keyframes rotate { 0% { transform: perspective(400px) rotateZ(20deg) rotateX(-40deg) rotateY(0); } 100% { transform: perspective(400px) rotateZ(20deg) rotateX(-40deg) rotateY(-360deg); } } .stars { transform: perspective(500px); transform-style: preserve-3d; position: absolute; perspective-origin: 50% 100%; left: 45%; animation: rotate 90s infinite linear; bottom: 0; } .star { width: 2px; height: 2px; background: #f7f7b6; position: absolute; left: 0; top: 0; backface-visibility: hidden; } </style> 7.8.3.identify.vue<template> <div class="s-canvas"> <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas> </div> </template> <script> export default{ name: 'SIdentify', props: { identifyCode: { type: String, default: '1234' }, fontSizeMin: { type: Number, default: 16 }, fontSizeMax: { type: Number, default: 40 }, backgroundColorMin: { type: Number, default: 180 }, backgroundColorMax: { type: Number, default: 240 }, colorMin: { type: Number, default: 50 }, colorMax: { type: Number, default: 160 }, lineColorMin: { type: Number, default: 40 }, lineColorMax: { type: Number, default: 180 }, dotColorMin: { type: Number, default: 0 }, dotColorMax: { type: Number, default: 255 }, contentWidth: { type: Number, default: 112 }, contentHeight: { type: Number, default: 38 } }, methods: { // 生成一个随机数 randomNum (min, max) { return Math.floor(Math.random() * (max - min) + min) }, // 生成一个随机的颜色 randomColor (min, max) { let r = this.randomNum(min, max) let g = this.randomNum(min, max) let b = this.randomNum(min, max) return 'rgb(' + r + ',' + g + ',' + b + ')' }, drawPic () { let canvas = document.getElementById('s-canvas') let ctx = canvas.getContext('2d') ctx.textBaseline = 'bottom' // 绘制背景 ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax) ctx.fillRect(0, 0, this.contentWidth, this.contentHeight) // 绘制文字 for (let i = 0; i < this.identifyCode.length; i++) { this.drawText(ctx, this.identifyCode[i], i) } this.drawLine(ctx) this.drawDot(ctx) }, drawText (ctx, txt, i) { ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax) ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei' let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1)) let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5) var deg = this.randomNum(-45, 45) // 修改坐标原点和旋转角度 ctx.translate(x, y) ctx.rotate(deg * Math.PI / 180) ctx.fillText(txt, 0, 0) // 恢复坐标原点和旋转角度 ctx.rotate(-deg * Math.PI / 180) ctx.translate(-x, -y) }, drawLine (ctx) { // 绘制干扰线 for (let i = 0; i < 8; i++) { ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax) ctx.beginPath() ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight)) ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight)) ctx.stroke() } }, drawDot (ctx) { // 绘制干扰点 for (let i = 0; i < 100; i++) { ctx.fillStyle = this.randomColor(0, 255) ctx.beginPath() ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI) ctx.fill() } } }, watch: { identifyCode () { this.drawPic() } }, mounted () { this.drawPic() } } </script> 7.8.4.request.jsimport axios from 'axios' import {Message} from 'element-ui' import cookie from 'js-cookie' // 创建axios实例 const service = axios.create({ baseURL: 'http://localhost:9090', // api 的 base_url timeout: 1000000 // 请求超时时间 }) // http request 拦截器 // 请求拦截器 service.interceptors.request.use( config => { // 可以在这里添加请求头等信息 // token 先不处理,后续使用时在完善 if (cookie.get('adminToken')) { config.headers['token'] = cookie.get('adminToken') } return config; }, err => { // 请求错误处理 console.log(err); return Promise.reject(err); } ); // http response 拦截器 // 响应拦截器 service.interceptors.response.use( response => { // 对响应数据做处理,例如只返回data部分 //const data = response.data; //return data; if (response.code === 208) { this.$message.warning(response.message) router.push('login') return } else { if (response.data.code === 200) { return response.data } else { Message({ message: response.data.message, type: 'error', duration: 5 * 1000 }) return Promise.reject(response.data) } } }, error => { // 响应错误处理 console.log('err' + error); // for debug //return Promise.reject(error); return Promise.reject(error.response); } ); export default service; 7.8.5.store.jsimport Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); // 登录验证 export default new Vuex.Store({ state: { user: false }, mutations: { // 登录 login(state, adminName) { state.adminName = adminName; localStorage.setItem("adminName", adminName); }, // 退出 logout(state, adminName) { state.adminName = ""; localStorage.setItem("adminName", ""); } } }) 7.8.6.util.js/** * 时间戳 * @param {*} timestamp 时间戳 */ const timestampToTime = (timestamp) => { let date = new Date(timestamp) //时间戳为10位需*1000,时间戳为13位的话不需乘1000 let Y = date.getFullYear() + '-' let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-' let D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ' let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':' let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':' let s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() return Y + M + D + h + m + s }; /** * 存储localStorage */ const setStore = (name, content) => { if (!name) return; if (typeof content !== 'string') { content = JSON.stringify(content); } window.localStorage.setItem(name, content); } /** * 获取localStorage */ const getStore = name => { if (!name) return; return window.localStorage.getItem(name); } /** * 删除localStorage */ const removeStore = name => { if (!name) return; window.localStorage.removeItem(name); } /** * 设置cookie **/ function setCookie(name, value, day) { let date = new Date(); date.setDate(date.getDate() + day); document.cookie = name + '=' + value + ';expires=' + date; }; /** * 获取cookie **/ function getCookie(name) { let reg = RegExp(name + '=([^;]+)'); let arr = document.cookie.match(reg); if (arr) { return arr[1]; } else { return ''; } }; /** * 删除cookie **/ function delCookie(name) { setCookie(name, null, -1); }; /** * 导出 **/ export default{ timestampToTime, setStore, getStore, removeStore, setCookie, getCookie, delCookie } 7.8.7.index.js//在路由文件router/index.js中配置 // 该文件专门用于创建整个应用的路由器 // 创建应用程序的路由器 import Vue from 'vue' import VueRouter from 'vue-router' // 此时就可以在Vue实例中配置路由器了 Vue.use(VueRouter) // 创建并暴露一个路由器 export default new VueRouter({ routes:[ { path:'/', name:'login', meta:{ title:"登录页" }, //redirect:'front/login', component:() => import('../view/login'), /*hidden: true, meta: { requireAuth: false //登录权限实现(登录拦截) }*/ }, { path:'/admin', name:'admin', meta:{ title:"首页" }, component:() => import('../view/admin'), children:[ { path:'/main', name:'main', meta:{ title:"首页" }, component:() => import('../view/main') }, //用户管理 { path:'/user', name:'user', meta:{ title:"用户管理:超级管理员:用户" }, component:() => import('../view/user/user') }, ], }, { path:'/admin', name:'admin', meta:{ title:"文件管理" }, component:() => import('../view/admin'), children:[ { path:'/main', name:'main', meta:{ title:"首页" }, component:() => import('../view/main') }, //用户管理 { path:'/user', name:'user', meta:{ title:"用户管理:超级管理员:用户" }, component:() => import('../view/user/user') }, //文件管理 { path:'/upload', name:'upload', meta:{ title:"文件管理:上传文件" }, component:() => import('../view/fastdfs/fastdfsuploadimg') }, { path:'/file', name:'file', meta:{ title:"文件管理:文件" }, component:() => import('../view/fastdfs/fastdfsimg') }, ], }, ], mode:'history', //去除#号 base: process.env.BASE_URL, }) 7.8.8.admin.jsimport request from "@/utils/request"; const api_name = '/admins' export default { //登录 loginAdmin(name,password){ return request({ url: `${api_name}/${name}/${password}`, method:'post' }) }, //获取当前用户信息 getAdminById(){ return request({ url: `${api_name}`, method:'get' }) } }7.8.9.menu.jsimport request from "@/utils/request"; const api_name = '/menus' export default { //获取菜单列表 getMenuByRoleId(){ return request({ url: `${api_name}`, method:'get' }) }, }7.8.10.main.js// 引入App 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' // 引入状态管理 import store from './utils/store' //引入qs模块发送post import qs from 'qs' Vue.prototype.$qs = qs import SIdentify from "@/view/code/identify"; Vue.use(SIdentify) // 过滤器 import * as custom from './utils/util' // 二维码生成器 import VueQriously from 'vue-qriously' Vue.use(VueQriously) Vue.config.silent = true // 设置全局变量 Vue.prototype.$baseImagePath = 'http://192.168.229.141' //把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 Object.keys(custom).forEach(key => { Vue.filter(key, custom[key]) }) // 创建和挂载根实例 new Vue({ store, //使用store vuex状态管理 router, //将路由器注入到new Vue实例中,建立关联 render: h => h(App), //将App组件放入容器中 data: { // 空的实例放到根组件下,所有的子组件都能调用 Bus: new Vue() } }).$mount('#app');7.9.效果展示八、总结案例有些不足功能太简单功能复杂可以做一个类似网盘的文件管理系统仅仅学习使用某些功能暂不深入开发有兴趣的伙伴可以尝试一番类似于阿里云ossendlendl
2024年07月25日
3 阅读
0 评论
0 点赞
2024-03-13
vue【opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ]】
@TOC一、Vue项目启动报错如下> front@0.1.0 serve > vue-cli-service serve Browserslist: caniuse-lite is outdated. Please run: npx browserslist@latest --update-db Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating INFO Starting development server... 10% building 2/5 modules 3 active ...rontsHospital\front\node_modules\eslint-loader\index.js??ref--14-0!D:\Vue\front\src\main.js E rror: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:68:19) at Object.createHash (node:crypto:138:10) at module.exports (D:\Vue\front\node_modules\webpack\lib\util\createHash.js:135:53) at NormalModule._initBuildHash (D:\Vue\front\node_modules\webpack\lib\NormalModule.js:417:16) at handleParseError (D:\Vue\front\node_modules\webpack\lib\NormalModule.js:471:10) at D:\Vue\front\node_modules\webpack\lib\NormalModule.js:503:5 at D:\Vue\front\node_modules\webpack\lib\NormalModule.js:358:12 at D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:373:3 at iterateNormalLoaders (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:214:10) at iterateNormalLoaders (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:221:10) at D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:236:3 at runSyncOrAsync (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:130:11) at iterateNormalLoaders (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:232:2) at Array.<anonymous> (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:205:4) at Storage.finished (D:\Vue\front\node_modules\enhanced-resolve\lib\CachedInputFileSystem.js:55:16) at D:\Vue\front\node_modules\enhanced-resolve\lib\CachedInputFileSystem.js:91:9 node:internal/crypto/hash:68 this[kHandle] = new _Hash(algorithm, xofLen); ^ Error: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:68:19) at Object.createHash (node:crypto:138:10) at module.exports (D:\Vue\front\node_modules\webpack\lib\util\createHash.js:135:53) at NormalModule._initBuildHash (D:\Vue\front\node_modules\webpack\lib\NormalModule.js:417:16) at handleParseError (D:\Vue\front\node_modules\webpack\lib\NormalModule.js:471:10) at D:\Vue\front\node_modules\webpack\lib\NormalModule.js:503:5 at D:\Vue\front\node_modules\webpack\lib\NormalModule.js:358:12 at D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:373:3 at iterateNormalLoaders (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:214:10) at Array.<anonymous> (D:\Vue\front\node_modules\loader-runner\lib\LoaderRunner.js:205:4) at D:\Vue\front\node_modules\enhanced-resolve\lib\CachedInputFileSystem.js:91:9 at D:\Vue\front\node_modules\graceful-fs\graceful-fs.js:123:16 at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read/context:68:3) { opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ], library: 'digital envelope routines', reason: 'unsupported', Node.js v20.11.0解决方式1:NodeJS版本过高解决方式2:添加项目启动参数在npm run serve项目启动前声明条件变量//windows $env:NODE_OPTIONS="--openssl-legacy-provider" npm run serve //Linux NODE_OPTIONS=--openssl-legacy-provider npm run servePS D:\Vue\front> $env:NODE_OPTIONS="--openssl-legacy-provider" PS D:\Vue\front> npm run serve DONE Compiled successfully in 12694ms 15:30:15 App running at: - Local: http://localhost:8080/ - Network: http://192.168.1.10:8080/ Note that the development build is not optimized. To create a production build, run npm run build. endl
2024年03月13日
82 阅读
0 评论
0 点赞
2024-03-09
vue循环指令el-table-column展示图片src路径拼接
在Vue中,使用el-table-column展示图片时,可以通过拼接图片路径来设置src属性。这里提供一个简单的例子: <template> <el-table :data="tableData" style="width: 100%"> <el-table-column prop="date" label="日期" width="180"></el-table-column> <el-table-column label="图标" width="180"> <template slot-scope="scope"> <img :src="getImageUrl(scope.row.icon)" alt="图标" /> </template> </el-table-column> <!-- 其他列 --> </el-table> </template> <script> export default { data() { return { tableData: [ { date: '2016-05-02', icon: 'image1' }, { date: '2016-05-04', icon: 'image2' }, // 其他数据... ], baseImagePath: 'path/to/your/images/' // 图片的基础路径 }; }, methods: { getImageUrl(icon) { return `${this.baseImagePath}${icon}.png`; // 拼接图片路径 } } }; </script>
2024年03月09日
121 阅读
0 评论
0 点赞
2024-02-27
正则表达式,将字符串分割两部分
@Test public void split() { String file_id = "group1/M01/00/00/wKjljWXLZxWAeK7JAAmOXAH6cKY805.png"; String[] splitStr = file_id.split("/"); String group_name = splitStr[0]; String remoteFileName = ""; for (int i = 1; i < splitStr.length; i++) { remoteFileName += splitStr[i]; if (i != splitStr.length - 1) { remoteFileName += "/"; } } System.out.println("group_name = " + group_name); System.out.println("remoteFileName = " + remoteFileName); }group_name = group1 remoteFileName = M01/00/00/wKjljWXLZxWAeK7JAAmOXAH6cKY805.png
2024年02月27日
111 阅读
0 评论
0 点赞
2024-02-13
文件上传下载-io-流的理解-笔记
流就是中间桥梁// 第一步 把文件转成输入流 InputStream is = file.getInputStream(); // 第二步 把 从输入流 读到 代码 byte[] buffer = new byte[1024]; int len = is.read(buffer); // 上面的信息量很大,首先buffer的长度是1024 这是为了方便读 // 其次, read这个方法的返回值是 实际独到的字节数,也就是说是 <= 1024的数字 // 所以, 想一次性把 输入流中的数据 全都读入到缓冲区buffer, 那buffer就要足够大, 占用内存也会很大 // 如果要把代码中的数据,再存入本地,那就需要 输出流 // 第一步: 创建一个输出流,传入的参数是目的地 OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); // 第二步: 写 os.write(buffer, 0, len); os.flush(); // 关闭资源 os.close(); is.close();
2024年02月13日
128 阅读
0 评论
0 点赞