IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> FastDFS笔记 -> 正文阅读

[系统运维]FastDFS笔记

介绍

FastDFS是一个轻量的分布式文件系统,对文件进行管理,因为是轻量型,所以不适合视频网站那样的单文件大容量存储

FastDFS有两个角色,跟踪器(tracker)主要做调度工作(负载均衡),存储节点(storage)存储文件,存储节点采用了分卷的形式(类似磁盘的CDEF盘),卷与卷之间是相互独立的,一个卷可能有多个服务器,起到了冗余备份和负载均衡作用,当存储空间不足时,可以增加新的卷来扩充容量

架构图

客户端上传文件时,给到Tracker,由Tracker来判断存储容量、存储位置、生成存储路径到DB等操作,如果能存储的话,将ip 端口 路径等信息给回client,然后client直接去找Storage cluster进行写入

客户端下载文件时,页面中有从DB中拿出来的路径(存储时Ttracker生成的),但不知道ip和端口,通过携带FastDFS中的 **文件标识(卷名+文件名)**去Tracker,Tracker给返回ip和端口,然后通过ip 端口 路径 直接访问存储节点。如果客户端知道ip和端口可以直接去访问storage。

存储节点的集群中有多个卷,每个卷又有多个主备,起到负载均衡和冗余备份的作用

在这里插入图片描述

一个卷下服务器的同步机制:一个卷内的服务器写入客户端文件时,向其他服务器进行广播,然后其他服务器进行同步,只有源头数据才广播,备份数据的写入不用广播。特殊的,当增加新的服务器时,由其中一台服务器将所有内容给这台新服务器

FastDFS搭建

基础配置

用两个Linux服务器,分别作为tracker和storage

安装包都放到/root/upload下
安装到/usr/local/fastdfs下
在这里插入图片描述
1.1安装FastDFS 依赖

FastDFS 是 C 语言开发的应用。安装必须使用 make、cmake 和 gcc 编译器。
yum install -y make cmake gcc gcc-c++

1.2解压FastDFS 核心库

libfastcommon 是从 FastDFS 和 FastDHT 中提取出来的公共 C 函数库
cd /root/upload
unzip libfastcommon-master.zip -d /usr/local/fastdfs

1.3进入解压后的目录

cd /usr/local/fastdfs/libfastcommon-master

1.4编译安装

libfastcommon 没有提供 make 命令安装文件。使用的是 shell 脚本执行编译和安装。shell

脚本为 make.sh
编译
./make.sh

安装
./make.sh install
有固定的默认安装位置。在/usr/lib64 和/usr/include/fastcommon 两个目录中。

1.5创建软连接

因为 FastDFS 主程序设置的 lib 目录是/usr/local/lib,所以需要创建软链接
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so ln -s /usr/lib64/libfastcommon.so /usr/lib/libfastcommon.so
ln -s /usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so ln -s /usr/local/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so

1.6解压FastDFS 主程序

本案例使用 5.08 版本 FastDFS。cd /root/upload
tar -zxf FastDFS_v5.08.tar.gz -C /usr/local/fastdfs

1.7进入FastDFS 主程序源码目录

cd /usr/local/fastdfs/FastDFS

1.8修改安装路径
此操作可选。在集群环境中使用默认安装路径安装。两种安装方式都尝试一下。
vi /usr/local/fastdfs/FastDFS/make.sh
TARGET_PREFIX=$ DESTDIR/usr -> TARGET_PREFIX=$DESTDIR/usr/local

1.9编译安装

./make.sh

如果出现下面错误
在这里插入图片描述

(1)运行如下代码:
yum -y install zlib zlib-devel pcre pcre-devel gcc gcc-c++ openssl openssl-devel libevent libevent-devel perl unzip net-tools wget

(2)需要清除编译,安装好以上运行环境,重新运行即可。
清除编译代码: ./make.sh clean

重新./make.sh

./make.sh install
安装后,FastDFS 主程序所在位置是:
/usr/local/bin - 可执行文件所在位置。默认安装在/usr/bin 中。
/etc/fdfs - 配置文件所在位置。就是默认位置。
/usr/local/lib64 - 主程序代码所在位置。默认在/usr/bin 中。
/usr/local/include/fastdfs - 包含的一些插件组所在位置。默认在/usr/include/fastdfs 中。

1.10FastDFS 安装后资源简介

1.10.1服务脚本

/etc/init.d/目录中,脚本文件是 - fdfs-storaged 和 fdfs-trackerd ls /etc/init.d/ | grep fdfs

1.10.2配置文件模板

/etc/fdfs/ 目 录 中 , 配 置 文 件 是 - client.conf.sample 、 storage.conf.sample 和
tracker.conf.sample ls /etc/fdfs/
tracker.conf.sample - 跟踪器服务配置文件模板
storage.conf.sample - 存储服务器配置文件模板
client.conf.sample - FastDFS 提供的命令行客户端配置文件模板。可以通过命令行测试
FastDFS 有效性。

1.10.3内置命令

/usr/local/bin/目录中。命令有若干。可通过命令在控制台访问 FastDFS。ls /usr/local/bin/ | grep fdfs
以上为通用安装,FastDFS 的跟踪服务和存储服务是通过配置实现的。后续内容为跟踪服务和存储服务的具体配置。

刚开始两者都需要搭建同样的环境,不同的是后边配置文件的配置

tracker 配置

只在 tracker server 节点中配置。

2.1创建跟踪服务配置文件

FastDFS 提供了配置文件模板,可以根据模板创建需要使用的配置文件。
cd /etc/fdfs
cp tracker.conf.sample tracker.conf

2.2修改配置文件

tracker.conf 配置文件用于描述跟踪服务的行为,需要进行下述修改:
vi /etc/fdfs/tracker.conf
port=22122 # 默认服务端口
base_path=/home/yuqing/fastdfs -> base_path=/fastdfs/tracker(自定义目录)
base_path 是 FastDFSTracker 启动后使用的根目录。也就是 data 和 logs 所在位置。

2.3创建自定义目录

为配置文件中定义的 base_path 变量创建对应的目录。
mkdir -p /fastdfs/tracker

2.4修改启动服务脚本

vi /etc/init.d/fdfs_trackerd
将 PRG=/usr/bin/fdfs_trackerd 修改为 PRG=/usr/local/bin/fdfs_trackerd

2.5启动服务

/etc/init.d/fdfs_trackerd start
启动成功后,配置文件中base_path 指向的目录中出现FastDFS 服务相关数据目录(data 目录、logs 目录)

2.6查看服务状态

/etc/init.d/fdfs_trackerd status ps aux | grep fdfs

2.7停止服务

/etc/init.d/fdfs_trackerd stop

2.8重启服务

/etc/init.d/fdfs_trackerd restart

2.9设置开启自启

vi /etc/rc.d/rc.local
新增内容 - /etc/init.d/fdfs_trackerd start

storage 配置

只在 storage server 中配置。192.168.2.100

3.1创建存储服务配置文件

FastDFS 提供了配置文件模板,可以根据模板创建需要使用的配置文件。
cd /etc/fdfs
cp storage.conf.sample storage.conf

3.2修改配置文件

storage.conf 配置文件用于描述存储服务的行为,需要进行下述修改:
vi /etc/fdfs/storage.conf
base_path=/home/yuqing/fastdfs -> base_path=/fastdfs/storage/base ( 自 定 义 目 录 ) store_path0=/home/yuqing/fastdfs -> store_path0=/fastdfs/storage/store( 自 定 义 目 录 ) tracker_server=192.168.2.109:22122 -> tracker_server=tracker 服务IP:22122
base_path - 基础路径。用于保存 storage server 基础数据内容和日志内容的目录。
store_path0 - 存储路径。是用于保存 FastDFS 中存储文件的目录,就是要创建 256*256
个子目录的位置。
base_path 和 store_path0 可以使用同一个目录。
tracker_server - 跟踪服务器位置。就是跟踪服务器的 ip 和端口。

3.3创建自定义目录

mkdir -p /fastdfs/storage/base mkdir -p /fastdfs/storage/store

3.4修改服务脚本

vi /etc/init.d/fdfs_storaged
将 PRG=/usr/bin/fdfs_storaged 修改为 PRG=/usr/local/bin/fdfs_storaged

3.5启动服务(要求tracker 服务必须已启动)

/etc/init.d/fdfs_storaged start
启动成功后,配置文件中 base_path 指向的目录中出现 FastDFS 服务相关数据目录(data 目录、logs 目录),配置文件中的 store_path0 指向的目录中同样出现 FastDFS 存储相关数据录(data 目录)。其中$store_path0/data/目录中默认创建若干子孙目录(两级目录层级总

计 256256 个目录),是用于存储具体文件数据的。
Storage 服务器启动比较慢,因为第一次启动的时候,需要创建 256
256 个目录。

3.6查看服务状态

/etc/init.d/fdfs_storaged status ps aux | grep fdfs

3.7停止服务

/etc/init.d/fdfs_storaged stop

3.8重启服务

/etc/init.d/fdfs_storaged restart

3.9设置开机自启

vi /etc/rc.d/rc.local
新增内容 - /etc/init.d/fdfs_storaged start

因启动前提为tracker 服务必须已启动,不推荐开启自启。

客户端基础配置

不是必须的。就是用于使用命令行测试 FastDFS 才需要配置的。

4.1创建客户端配置文件

在 tracker 服务结点所在服务器中配置客户端。同样通过配置文件模板创建对应配置文
件。
cd /etc/fdfs
cp client.conf.sample client.conf

4.2修改配置文件

client.conf 配置文件中主要描述客户端的行为,需要进行下述修改:
vi /etc/fdfs/client.conf
base_path=/home/yuqing/fastdfs -> base_path=/fastdfs/client ( 自 定 义 目 录 ) tracker_server=192.168.2.109:22122 -> tracker_server=tracker 服 务 IP:22122
base_path - 就是客户端命令行执行过程时临时数据存储位置。

4.3创建自定义目录

mkdir -p /fastdfs/client

5控制台测试 FastDFS

命令所在: /usr/local/bin 目录。 (如果在安装 FastDFS 过程中,没有修改 make.sh 文件中的 TARGET_PREFIX 属性值,命令所在为/usr/bin 目录)

5.1上传文件

/usr/local/bin/fdfs_upload_file /etc/fdfs/client.conf /要上传的文件
上传结束后, 返回 group1/M00/00/00/xxxxxxxxxx.xxx , 检查 storage 服务结点中的
$ store_path0/data/00/00/ 目录中是否有上传的文件(一般情况上传的文件按顺序保存在
$ store_path0/data/00/00/目录中,不能完全保证)。
在这里插入图片描述

卷名 : group1
文件名 : M00/00/00/wKjniGD3ez-AFeyzAAeItTLD40o493.zip
其中 M00 是一个虚拟目录,相当于 windows 中的快捷方式,引用的是$ store_path0/data 目录。
在这里插入图片描述

5.2删除文件

/usr/local/bin/fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/xxxxxxx.xxx
删除结束后,检查$store_path0/data/00/00/目录中是否还有文件。

安装Nginx

nginx+FastDFS ,nginx作为反向代理作为FastDFS的代理服务器来处理web的HTTP请求

在安装 nginx 应用的时候,同时要在 nginx 中增加一个 FastDFS 的组件。 Module。

tar -zxf fastdfs-nginx-module_v1.16.tar.gz -C /usr/local/fastdfs
cd /usr/local/fastdfs/fastdfs-nginx-module/src 
vi /usr/local/fastdfs/fastdfs-nginx-module/src/config
CORE_INCS="$CORE_INCS /usr/local/include/fastdfs /usr/include/fastcommon/"

安装 Nginx 需要的依赖

yum install -y gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel

解压nginx

tar -zxf nginx-1.8.0.tar.gz -C /usr/local/fastdfs/

进入nginx

cd /usr/local/fastdfs/nginx-1.8.0/

在nginx目录下输入下边配置

./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi \
--add-module=/usr/local/fastdfs/fastdfs-nginx-module/src

在这里插入图片描述

创建目录 Nginx 运行时需要创建若干临时文件,如果默认安装不需创建此目录。
mkdir -p /var/temp/nginx

将/nginx目录下/objs/Makefile中的 -Werrori 删除。

vim  objs/Makefile

在nginx目录下输入命令:vim src/os/unix/ngx_user.c,将下图内容注释,退出保存即可正常编译在这里插入图片描述

编译安装
make
make install

配置 fastdfs-nginx-module 模块配置文件

复制配置文件/usr/local/fastdfs/fastdfs-nginx-module/src/mod_fastdfs.conf 到/etc/fdfs 目录

cp /usr/local/fastdfs/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/

cd /etc/fdfs/
修改配置文件 mod_fastdfs.conf vi mod_fastdfs.conf
vi mod_fastdfs.conf
源配置:

connect_timeout=2 #连接超时时间,单位秒 
tracker_server=tracker:22122 #tracker 服务结点 
url_have_group_name = false #URL 中是否包含 group 名称 
store_path0=/home/yuqing/fastdfs # storage 服务结点的存储位置,与配置 storage 结点一 致
connect_timeout=10 
tracker_server=tracker的ip:22122
url_have_group_name = true
store_path0=/fastdfs/storage/store

提供 FastDFS 需要的HTTP 配置文件

复制 FastDFS 安装包中的两个配置文件(http.conf 和 mime.types)到/etc/fdfs 目录中

cp /usr/local/fastdfs/FastDFS/conf/http.conf /etc/fdfs/ 
cp /usr/local/fastdfs/FastDFS/conf/mime.types /etc/fdfs/

6.3.10创建nginx 启动需要的软连接

创建软连接

ln -s /usr/local/lib64/libfdfsclient.so /usr/lib64/libfdfsclient.so

nginx 启动后,会在默认的/usr/lib64 目录中查找需要的so 文件。如果在安装FastDFS 时,修改了make.sh 文件中的TARGET_PREFIX 参数,则必须创建此软连接

创建网络访问存储服务的软连接

ln -s /fastdfs/storage/store/data/ /fastdfs/storage/store/data/M00

在上传文件到 FastDFS 后,FastDFS 会返回 group1/M00/00/00/xxxxxxxxxx.xxx。其中group1 是卷名,在 mod_fastdfs.conf 配置文件中已配置了 url_have_group_name,以保证 URL 解析正确。而其中的 M00 是 FastDFS 保存数据时使用的虚拟目录,需要将这个虚拟目录定位到真实数据目录上。

修改nginx 配置文件

cd /usr/local/nginx/conf vi nginx.conf
参考修改配置:(部分配置信息,不要完整复制)

user	root; # Nginx 需要访问 linux 文件系统,必须有文件系统的权限。User root 代表
nginx 访问文件系统的权限是 root 用户权限。如果不开启权限,可能有 404 访问错误。
server{
	listen	8888; # storage 配置中,有 http.server_port=8888 的配置信息,必须一致。配置文件是/etc/fdfs/storaged.conf
		server_name	localhost;
		location ~/group([0-9])/M00{
			ngx_fastdfs_module;
		}
}

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

测试WEB 访问存储服务中的文件
上传一个图片到storage
在这里插入图片描述
group1/M00/00/00/wKjniGD3iSGAazfTAAMRtuDuhdI540.jpg
在这里插入图片描述

重启fastdfs,启动nginx

/etc/init.d/fdfs_storaged restart

使用浏览器查看 FastDFS 中保存的文件:
http://192.168.231.136:8888/group1/M00/00/00/wKjniGD3iSGAazfTAAMRtuDuhdI540.jpg

在这里插入图片描述

Java客户端

conf配置

在这里插入图片描述

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

    <groupId>org.example</groupId>
    <artifactId>fastdfs</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>cn.bestwu</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
    </dependencies>


</project>
# 连接超时, 单位是秒
connect_timeout = 10
# 网络超时, 单位是秒
network_timeout = 30
# 字 符 集 设 置
charset = UTF-8
# 必须和tracker server 中的/etc/fdfs/tracker.conf 配置文件中的http.server_port=8080 配置一致
http.tracker_http_port = 8080
# tracker server 的访问 IP 和 Port。
tracker_server = 192.168.231.135:22122
package cn.wit.test;

import org.csource.common.MyException;
import org.csource.fastdfs.*;

import org.csource.common.MyException; import org.csource.fastdfs.*;
import java.io.IOException;

public class TestInitClient {
    // 配置文件所在位置。
    private static final String configFile = "src/main/resources/test/fdfs.conf";
    public static void main(String[] args) throws IOException, MyException {
       
            // 使用 conf 配置文件初始化环境。MyException 是 FastDFS 定义的自定义异常,其处理的就是加载链接超时,网络超时,tracker 服务器列表加载异常等。
            ClientGlobal.init(configFile);
            //绑定tracker服务器的ip和端口
            TrackerClient trackerClient=new TrackerClient();
            // 创建 tracker 服务器
            TrackerServer trackerServer = trackerClient.getConnection();
            // 创建 storage 服务器的链接对象
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            // 创建 storage 服务器的客户端操作对象。可以实现文件的读写操作。
            StorageClient storageClient = new StorageClient(trackerServer, storageServer);

            System.out.println("TrackerClient : " + trackerClient);
            System.out.println("TrackerServer : " + trackerServer);
            System.out.println("StorageServer : " + storageServer);
            System.out.println("StorageClient : " + storageClient);
    }

}

properties配置

fdfs.properties

# 连接超时, 单位是秒
fastdfs.connect_timeout_in_seconds=10
# 网络超时, 单位是秒
fastdfs.network_timeout_in_seconds=30
# 字符集设置
fastdfs.charset=UTF-8
# 必须和tracker server 中的/etc/fdfs/tracker.conf 配置文件中的http.server_port=8080 配置
一致
fastdfs.http_tracker_http_port=8080

# tracker server 的访问 IP 和 Port。
fastdfs.tracker_servers=192.168.231.135:22122
package cn.wit.test;

import org.csource.common.MyException;
import org.csource.fastdfs.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class TestInitClientByProperties {
    private final static Properties props = new Properties();
    /**
     *根据 Properties,加载配置文件。
     *加载 properties 配置文件,生成一个 Properties 对象。再实现环境的初始化。
     */

    public static void main(String[] args) throws IOException, MyException {
        // 使用 Properties 配置文件初始化环境。
        InputStream in=TestInitClientByProperties.class.getClassLoader().getResourceAsStream("test/fdfs.properties");
        props.load(in);
        ClientGlobal.initByProperties(props);
        // this.initByProperties();
        TrackerClient trackerClient	=new TrackerClient();
        // 创建 tracker 服务器的链接对象
        TrackerServer trackerServer = trackerClient.getConnection();
        // 创建 storage 服务器的链接对象
        StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
        // 创建 storage 服务器的客户端操作对象。可以实现文件的读写操作。
        StorageClient storageClient = new StorageClient(trackerServer, storageServer);

        System.out.println("TrackerClient : " + trackerClient);
        System.out.println("TrackerServer : " + trackerServer);
        System.out.println("StorageServer : " + storageServer);
        System.out.println("StorageClient : " + storageClient);
    }


}

工具类

用fdfs.conf配置

public class fdfsUtils {
    private static final String configFile = "src/main/resources/test/fdfs.conf";
    private static StorageClient storageClient;
    static{
        try {
            // 使用 conf 配置文件初始化环境。就是加载链接超时,网络超时,tracker 服务器列表等。
            ClientGlobal.init(configFile);
            // this.initByProperties();
            TrackerClient trackerClient	=new TrackerClient();
            // 创建 tracker 服务器的链接对象
            TrackerServer trackerServer = trackerClient.getConnection();
            // 创建 storage 服务器的链接对象
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            // 创建 storage 服务器的客户端操作对象。可以实现文件的读写操作。
            storageClient = new StorageClient(trackerServer, storageServer);
        }catch(Exception e){ e.printStackTrace();
            throw new ExceptionInInitializerError(e);
        }
    }

    public static StorageClient getStorageClient(){ return storageClient;
    }

    public static void main(String[] args) { System.out.println(getStorageClient());
    }
}

上传文件

在这里插入图片描述

package cn.wit.test;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.StorageClient;

public class Upload {

    public static void main(String[] args) {
        String fileIds[] = testUpload("testFile", "b.jpg", "jpg");
        if(fileIds == null){
            System.out.println("文件上传失败!");
        }
        System.out.println(fileIds.length);
        System.out.println("组名/卷名:" + fileIds[0]);
        System.out.println("路径: " + fileIds[1]);
    }

    public static String[] testUpload(String dirName, String fileName, String fileType){
        StorageClient storageClient = fdfsUtils.getStorageClient();
        try{
            // 要上传的文件的元数据。可以自定义。
           NameValuePair nvp [] = new NameValuePair[]{ new NameValuePair("filename", fileName)};
            /*
            *String[] upload_file(String fileName, String fileExtName, NameValuePair[] metaList)
            *fileName - 要上传的本地文件,包含路径地址。
            *fileExtName - 文件后缀名;可以不传递,也就是传递一个 null。FastDFS
            可以自动的在文件名中截取文件类型。
            *metaList - 文件的描述元数据。
            *返回的结果是一个字符串数组。长度为 2。0 位置是卷名-group1,1 位置是文件名-M00/00/00/xxxxxx
            */
            String fileIds[] = storageClient.upload_file(dirName+"/"+fileName, fileType, nvp);
            return fileIds;
    }catch(Exception e){
            e.printStackTrace();
    }
            return null;
    }
}

在这里插入图片描述

下载文件

加载上面上传的文件

package cn.wit.test;

import org.apache.commons.io.IOUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.StorageClient;

import java.io.FileOutputStream;

public class Download {
    public static void main(String[] args) {
        testDownload("group1", "M00/00/00/wKjniGD4IAaADlTJAAAm5FZR7yg812.jpg");
    }
    public static void testDownload(String groupName, String remoteFileName){
        StorageClient storageClient = fdfsUtils.getStorageClient();
        try{
            /*
             *byte[] download_file(String groupName, String remoteFileName)
             *groupName -  要下载的文件的卷名
             *remoteFileName -  要下载的文件的远程文件名
             *返回值是要下载的文件的字节数组。
             */
            byte[] buff = storageClient.download_file(groupName, remoteFileName);
            /*
             *NameValuePair[] get_metadata(String groupName, String remoteFileName)
             *groupName -  要查找元数据的文件的卷名
             *remoteFileName - 要查找元数据的文件的远程文件名
             *返回值是要查找元数据的文件的元数据数组。
             */
            NameValuePair[]	metaList=storageClient.get_metadata(groupName,remoteFileName);
            String fileName = null;
            for(int i = 0; i < metaList.length; i++){
                NameValuePair nvp = metaList[i];
                if(nvp.getName().equals("filename"))
                fileName = nvp.getValue();
            }
            IOUtils.write(buff, new FileOutputStream("downloadFile/"+ fileName));
            System.out.println("下载完成");
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
}

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

删除文件

package cn.wit.test;

import org.csource.fastdfs.StorageClient;

public class Delete {

    public static void main(String[] args) {
        testDelete("group1", "M00/00/00/wKjniGD4IAaADlTJAAAm5FZR7yg812.jpg");
    }
    public static void testDelete(String groupName, String remoteFileName){
        StorageClient storageClient = fdfsUtils.getStorageClient();
        try{
            /*
            int delete_file(String groupName, String remoteFileName)
            }
            *groupName - 要删除的文件卷名
            *remoteFileName - 要删除的文件的远程文件名
            *返回 0 为成功删除, 其他为删除失败。
            */
            int flag = storageClient.delete_file(groupName, remoteFileName);
            System.out.println(flag == 0 ? "删除成功" : "删除失败");
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}


在这里插入图片描述

web中使用

在这里插入图片描述

ssm+maven

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

  <groupId>cn.wit</groupId>
  <artifactId>webfdfs</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>webfdfs Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>cn.bestwu</groupId>
      <artifactId>fastdfs-client-java</artifactId>
      <version>1.27</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- MyBatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    <!-- mybatis 整合 spring 插件 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!-- mysql 数据库驱动 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.39</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.0.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>4.1.6</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.5</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.4</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>webfdfs</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <plugin>
          <groupId>org.apache.tomcat.maven</groupId>
          <artifactId>tomcat7-maven-plugin</artifactId>
          <configuration>
            <port>8080</port>
            <path>/</path>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <configuration>
          <port>8080</port>
          <path>/</path>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

数据库

drop table if exists t_files;
create table t_files(
id bigint not null auto_increment primary key,
file_name varchar(255), --  文件的原始名称
group_name varchar(32), -- 文件在 FastDFS 中的卷名
remote_file_name varchar(255), -- 文件在 FastDFS 中的文件名, 
file_path varchar(255) -- 文件在 FastDFS 中的路径, 就是卷名+远程文件名
);

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Archetype Created Web Application</display-name>
  <welcome-file-list>
    <welcome-file>/index</welcome-file>
  </welcome-file-list>

  <filter>
    <filter-name>charsetFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>charsetFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:/applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

applicationContext spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <mvc:annotation-driven/>

    <context:component-scan base-package="cn.wit"></context:component-scan>

    <!-- 文件上传的转换器 -->
    <bean	id="multipartResolver"
             class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 限制文件上传大小 -->
        <property name="maxUploadSize" value="2048000" />
        <!-- 内存缓存大小 -->
        <property name="maxInMemorySize" value="2048" />
        <!-- 处理文本文件的字符集 -->
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <!-- 连接池数据源 -->
    <bean	id="dataSource"
             class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/login?useSSL=false" />
        <property name="username" value="root" />
        <property name="password" value="wityy" />
    </bean>

    <!-- 会话工厂 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations">
            <array>
                <value>classpath:cn.wit.mapper/FileInfoMapper.xml</value>
            </array>
        </property>
    </bean>

    <!-- Mapper 工厂 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <property name="basePackage" value="cn.wit.mapper" />
    </bean>

    <!-- 事务管理器 -->
    <bean	id="transactionManager"
             class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
        </tx:attributes>
    </tx:advice>

    <!-- 事务切面 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution( * cn.wit.service.*.*(..))"/>
    </aop:config>

</beans>

conf配置文件

# 连接超时, 单位是秒
connect_timeout = 10
# 网络超时, 单位是秒
network_timeout = 30
# 字 符 集 设 置
charset = UTF-8
# 必须和tracker server 中的/etc/fdfs/tracker.conf 配置文件中的http.server_port=8080 配置一致
http.tracker_http_port = 8080
# tracker server 的访问 IP 和 Port。
tracker_server = 192.168.231.135:22122

工具类

package cn.wit.utils;

import org.csource.common.NameValuePair; import org.csource.fastdfs.*;

import java.io.ByteArrayInputStream; import java.io.InputStream;

// FastDFS 的工具类
public class FastDFSCommonsUtils {
    private static StorageClient storageClient =null;
    static{
        try{
            ClientGlobal.init(Thread.currentThread().getContextClassLoader().getResource("").getPath()
                            +"/fdfs.conf");
            TrackerClient trackerClient = new TrackerClient();
            TrackerServer trackerServer = trackerClient.getConnection();
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            storageClient = new StorageClient(trackerServer, storageServer);
        }catch(Exception e){ e.printStackTrace();
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     *上传文件的方法
     *@param inputStream 要上传的文件的输入流,用于读取文件内容
     *@param fileName 要上传的文件的原始文件名
     *@return 上传的结果
     */
    public static String[] upload(InputStream inputStream, String fileName){
        try {
            //文件描述元数据
            NameValuePair[] metaList = new NameValuePair[]{ new NameValuePair("filename", fileName)};
            //后缀名
            String extFileName = fileName.substring(fileName.lastIndexOf(".") + 1);
            //存储文件字节数组
            byte[] datas = new byte[inputStream.available()];
            inputStream.read(datas);
            //返回的fastdfs路径
            String[] result = storageClient.upload_file(datas, extFileName, metaList);
            System.out.println(result[0].toString());
            System.out.println(result[1].toString());
            return result;

        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     *下载文件
     *@return 要下载的文件的输入流
     */
    public static InputStream download(String groupName, String remoteFileName){
        try{
            byte[] datas = storageClient.download_file(groupName,remoteFileName);
            return new ByteArrayInputStream(datas);
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

文件实体类

package cn.wit.entity;
import java.io.Serializable; import java.util.Objects;

// 文件相关信息的实体类
public class FileInfo implements Serializable {
    private Long id;
    private String fileName;
    private String groupName;
    private String remoteFileName;
    private String filePath;

    @Override
    public boolean equals(Object o) { if (this == o) return true;
        if (o == null || getClass() != o.getClass())
            return false;
        FileInfo fileInfo = (FileInfo) o;
        return Objects.equals(id, fileInfo.id) &&Objects.equals(fileName, fileInfo.fileName) && Objects.equals(groupName, fileInfo.groupName) && Objects.equals(remoteFileName, fileInfo.remoteFileName) && Objects.equals(filePath, fileInfo.filePath);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, fileName, groupName, remoteFileName, filePath);
    }

    public Long getId() { return id;
    }

    public void setId(Long id) { this.id = id;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public String getRemoteFileName() {
        return remoteFileName;
    }

    public void setRemoteFileName(String remoteFileName) {
        this.remoteFileName = remoteFileName;
    }

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
}

mapper层

package cn.wit.mapper;

import cn.wit.entity.FileInfo;

import java.util.List;
// 文件数据的数据访问接口
public interface FileInfoMapper {
    /**
     *新增文件相关数据到数据库
     *@param fileInfo 要新增的数据
     */
    void insertFileInfo(FileInfo fileInfo);

    /**
     *查询所有的文件相关数据
     *@return
     */
    List<FileInfo> selectFileInfos();

    /**
     *主键查询详情
     *@param id
     *@return
     */
    FileInfo selectById(Long id);
}
<?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="cn.wit.mapper.FileInfoMapper">
            <insert id="insertFileInfo">
                insert into t_files(id, file_name, group_name, remote_file_name, file_path) values(default, #{fileName}, #{groupName}, #{remoteFileName}, #{filePath})
            </insert>
            <select id="selectFileInfos" resultType="cn.wit.entity.FileInfo">
                select id, file_name as fileName, group_name as groupName, remote_file_name as remoteFileName,
                file_path as filePath from t_files
            </select>
            <select id="selectById" resultType="cn.wit.entity.FileInfo">
                select id, file_name as fileName, group_name as groupName, remote_file_name as remoteFileName,
                file_path as filePath from t_files
                where id = #{id}
            </select>

        </mapper>

service

package cn.wit.service;

import cn.wit.entity.FileInfo;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;

// 文件处理服务接口
public interface FileService {
    /**
     *上传文件到 FastDFS 中,把文件的一些相关数据保存到数据库中。
     *@param file -  要上传的文件
     *@return true -  成功 ; false -  失败
     */
    boolean uploadFile(MultipartFile file);

    /**
     *查询所有的可下载的文件的数据
     *@return 数据库中的数据集合
     */
    List<FileInfo> getFileInfos();
    /**
     *获取要下载的文件
     *@param fileInfoId 文件在数据库中的主键
     *@return 要下载的文件的输入流
     */
    InputStream getFile(Long fileInfoId);

    FileInfo getFileInfoById(Long fileInfoId);
}

service.impl

package cn.wit.service.impl;

import cn.wit.entity.FileInfo;
import cn.wit.mapper.FileInfoMapper;
import cn.wit.service.FileService;
import cn.wit.utils.FastDFSCommonsUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;

// 文件处理服务实现
@Service
public class FileServiceImpl implements FileService {
    @Autowired
    private FileInfoMapper fileInfoMapper;
    @Override
    public boolean uploadFile(MultipartFile file) {
        try {
            String[] result=FastDFSCommonsUtils.upload(file.getInputStream(),file.getOriginalFilename());
            if(result == null){
            // 上传失败
                return false;
            }
            // System.out.println(Arrays.toString(result));
            // 将文件相关信息保存到数据库
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(file.getOriginalFilename());
            fileInfo.setGroupName(result[0]);
            fileInfo.setRemoteFileName(result[1]);
            fileInfo.setFilePath(result[0]+"/"+result[1]);
            this.fileInfoMapper.insertFileInfo(fileInfo);
            return true;
        }catch (Exception e){ // 上传失败
            e.printStackTrace();
            return false;
        }
}

    @Override
    public List<FileInfo> getFileInfos() {
        return this.fileInfoMapper.selectFileInfos();
    }

    @Override
    public InputStream getFile(Long fileInfoId) {
    // 访问数据库,获取要下载的文件的详情信息
        FileInfo fileInfo = this.fileInfoMapper.selectById(fileInfoId);
    // 访问 FastDFS,获取要下载的文件的具体内容
        InputStream	inputStream= FastDFSCommonsUtils.download(fileInfo.getGroupName(), fileInfo.getRemoteFileName());
    // 返回
        return inputStream;
    }

    @Override
    public FileInfo getFileInfoById(Long fileInfoId) {
        return this.fileInfoMapper.selectById(fileInfoId);
    }
}

controller

package cn.wit.controller;

import cn.wit.entity.FileInfo;
import cn.wit.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.InputStream; import java.io.OutputStream; import java.util.List;

// 文件控制器,实现上传、下载功能
@Controller
public class FileController {
    @Autowired
    private FileService fileService;
        /**
         *文件上传
         *@param uploadFile 要上传的文件
         *@return
         */
        @RequestMapping("/uploadFile")
        public String uploadFile(MultipartFile uploadFile){
            //	System.out.println("上传文件的原始名称 - " + uploadFile.getOriginalFilename());
            //	System.out.println("上传文件的大小 - " + uploadFile.getSize());
            // 上传文件到 FastDFS 中
            boolean flag = this.fileService.uploadFile(uploadFile);
            System.out.println(flag ? "上传成功" : "上传失败");

            return "redirect:/index";
        }

        /**
         *进入到首页面的方法。
         *在进入首页面之前,需要先访问数据库,查询可以下载的文件的相关信息。
         *@return
         */
        @RequestMapping(value = {"/", "/index"})
        public String toIndex(Model model){
            List<FileInfo> list = this.fileService.getFileInfos();
            model.addAttribute("list", list);
            return "forward:/index.jsp";
        }

    /***下载文件
     *@param fileInfoId 要下载的文件在数据库中的相关数据主键
     */
    @RequestMapping(value="/downloadFile/{fileInfoId}")
    public void downloadFile(@PathVariable("fileInfoId") Long fileInfoId, HttpServletResponse response){
            // 获取要传递给客户端的文件数据
                InputStream inputStream = null;
                try {
                inputStream = this.fileService.getFile(fileInfoId);
                FileInfo fileInfo = this.fileService.getFileInfoById(fileInfoId);
            // 设置响应头, 响应为下载,下载的文件名是什么
                response.setContentType("application/octet-stream");
                response.setHeader("content-disposition", "attachement;filename="+fileInfo.getFileName());

            // 通过输出流输出文件内容到客户端
                OutputStream outputStream = response.getOutputStream();

                byte[] temp = new byte[512];
                int len = 0;
                while((len = inputStream.read(temp)) != -1){
                    outputStream.write(temp, 0, len);
                }
                // 刷新缓存
                    outputStream.flush();
                }catch(Exception e){
                        e.printStackTrace();
                }finally {
                    if(inputStream != null){ // 回收资源
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>FastDFS WEB 应用</title>
</head>
<body>
<center>
    <form method="post" enctype="multipart/form-data" action="/uploadFile"> 选 择 要 上 传 的 文 件 :<input type="file" name="uploadFile"> &nbsp;&nbsp;&nbsp;
        <input type="submit" value="上传">
    </form>
    <table border="1" style="width: 500px">
        <thead>
        <tr>
            <th>文件原始名称</th>
            <th>文件卷标名称</th>
            <th>文件远程名称</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        <c:forEach items="${list}" var="fileInfo">
        <tr>
            <th>${fileInfo.fileName}</th>
            <th>${fileInfo.groupName}</th>
            <th>${fileInfo.remoteFileName}</th>
            <th><a href="/downloadFile/${fileInfo.id}">下载</a>&nbsp;&nbsp;&nbsp;
                <a href="http://192.168.231.136:8888/${fileInfo.filePath}"target="_blank">预览</a></th>
        </tr>
        </c:forEach>
        </tbody>
    </table>
</center>
</body>
</html>

在这里插入图片描述

预览
在这里插入图片描述

下载

在这里插入图片描述

过程中报错

nginx重启报错 nginx: [emerg] open() “/var/run/nginx/nginx.pid” failed (2: No such file or directory)

解决方案

配置注意

maven项目是以resources文件夹为根目录,配置文件都要放在resources下

spring配置文件在resources下
在这里插入图片描述
resource下cn.wit.mapper包下 mapper配置文件
在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 14:38:04  更:2021-07-22 14:41:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 16:32:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码