1 时序图

2 流程说明

  1. 客户端访问Tracker
  2. Tracker 返回Storage的ip和端口
  3. 客户端直接访问Storage,把文件内容和元数据发送过去。
  4. Storage返回文件存储id。包含了组名和文件名


七、Fastdfs-java-client

1 添加依赖

<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>
</dependencies> `


2 编写配置文件

文件名:fdfs_client.conf

修改成自己的tracker服务器ip

connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.93.10:22122


3 导入工具类

在com.utils.FastDFSClient 下粘贴配置工具类

package com.msb.utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.lang3.StringUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;

/**
* FastDFS分布式文件系统操作客户端.
*/
public class FastDFSClient {

private static final String CONF_FILENAME = Thread.currentThread().getContextClassLoader().getResource("").getPath() + "fdfs_client.conf";

private static StorageClient storageClient = null;

/**
* 只加载一次.
*/
static {
try {
ClientGlobal.init(CONF_FILENAME);
TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
TrackerServer trackerServer = trackerClient.getConnection();
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient = new StorageClient(trackerServer, storageServer);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
*
* @param inputStream
* 上传的文件输入流
* @param fileName
* 上传的文件原始名
* @return
*/
public static String[] uploadFile(InputStream inputStream, String fileName) {
try {
// 文件的元数据
NameValuePair[] meta_list = new NameValuePair[2];
// 第一组元数据,文件的原始名称
meta_list[0] = new NameValuePair("file name", fileName);
// 第二组元数据
meta_list[1] = new NameValuePair("file length", inputStream.available()+"");
// 准备字节数组
byte[] file_buff = null;
if (inputStream != null) {
// 查看文件的长度
int len = inputStream.available();
// 创建对应长度的字节数组
file_buff = new byte[len];
// 将输入流中的字节内容,读到字节数组中。
inputStream.read(file_buff);
}
// 上传文件。参数含义:要上传的文件的内容(使用字节数组传递),上传的文件的类型(扩展名),元数据
String[] fileids = storageClient.upload_file(file_buff, getFileExt(fileName), meta_list);
return fileids;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}

/**
*
* @param file
* 文件
* @param fileName
* 文件名
* @return 返回Null则为失败
*/
public static String[] uploadFile(File file, String fileName) {
FileInputStream fis = null;
try {
NameValuePair[] meta_list = null; // new NameValuePair[0];
fis = new FileInputStream(file);
byte[] file_buff = null;
if (fis != null) {
int len = fis.available();
file_buff = new byte[len];
fis.read(file_buff);
}

String[] fileids = storageClient.upload_file(file_buff, getFileExt(fileName), meta_list);
return fileids;
} catch (Exception ex) {
return null;
}finally{
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**
* 根据组名和远程文件名来删除一个文件
*
* @param groupName
* 例如 "group1" 如果不指定该值,默认为group1
* @param remoteFileName
* 例如"M00/00/00/wKgxgk5HbLvfP86RAAAAChd9X1Y736.jpg"
* @return 0为成功,非0为失败,具体为错误代码
*/
public static int deleteFile(String groupName, String remoteFileName) {
try {
int result = storageClient.delete_file(groupName == null ? "group1" : groupName, remoteFileName);
return result;
} catch (Exception ex) {
return 0;
}
}

/**
* 修改一个已经存在的文件
*
* @param oldGroupName
* 旧的组名
* @param oldFileName
* 旧的文件名
* @param file
* 新文件
* @param fileName
* 新文件名
* @return 返回空则为失败
*/
public static String[] modifyFile(String oldGroupName, String oldFileName, File file, String fileName) {
String[] fileids = null;
try {
// 先上传
fileids = uploadFile(file, fileName);
if (fileids == null) {
return null;
}
// 再删除
int delResult = deleteFile(oldGroupName, oldFileName);
if (delResult != 0) {
return null;
}
} catch (Exception ex) {
return null;
}
return fileids;
}

/**
* 文件下载
*
* @param groupName 卷名
* @param remoteFileName 文件名
* @return 返回一个流
*/
public static InputStream downloadFile(String groupName, String remoteFileName) {
try {
byte[] bytes = storageClient.download_file(groupName, remoteFileName);
InputStream inputStream = new ByteArrayInputStream(bytes);
return inputStream;
} catch (Exception ex) {
return null;
}
}

public static NameValuePair[] getMetaDate(String groupName, String remoteFileName){
try{
NameValuePair[] nvp = storageClient.get_metadata(groupName, remoteFileName);
return nvp;
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}

/**
* 获取文件后缀名(不带点).
*
* @return 如:"jpg" or "".
*/
private static String getFileExt(String fileName) {
if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
return "";
} else {
return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
}
}
}

4 编写测试代码

随意新建一个包含主方法的类。com.msb.MyMain

public class MyMain {
public static void main(String[] args) {
try {
File file = new File("D:/b.png");
InputStream is = new FileInputStream(file);
String fileName = UUID.randomUUID().toString()+".png";
String[] result = FastDFSClient.uploadFile(is, fileName);
System.out.println(Arrays.toString(result));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

八、文件下载

1 时序图


2 下载说明

  1. client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
  2. tracker返回一台可用的storage;
  3. client直接和storage通讯完成文件下载。

3 代码实现

直接使用工具方法完成下载。

try {
InputStream is = FastDFSClient.downloadFile("group1", "M00/00/00/wKg0gF3zAKCARs6kAAASjQVYlWA098.png");
OutputStream os = new FileOutputStream(new File("D:/jqk.png"));
int index = 0 ;
while((index = is.read())!=-1){
os.write(index);
}
os.flush();
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}

九、Nginx简介

1 简介

FastDFS是没有文件访问功能的,需要借助其他工具实现图片HTTP访问的。Nginx就具备代理虚拟机主机功能。

​ Nginx (engine x) 是一个高性能的​​HTTP​​​和​​反向代理​​​服务。Nginx是由伊戈尔·赛索耶夫为​​俄罗斯​​访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。

​ Nginx 是一个很强大的高性能​​Web​​​和​​反向代理​​​服务,它具有很多非常优越的特性:在连接高并发的情况下,Nginx是​​Apache​​服务不错的替代品:Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一。

2 代理方式

2.1 正向代理

正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

2.2 反向代理

​ 反向代理(Reverse Proxy)方式是指以​​代理服务器​​来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

2.3 二者之间的区别

位置不同 正向代理,架设在客户机和目标主机之间; 反向代理,架设在服务器端;

代理对象不同 正向代理,代理客户端,服务端不知道实际发起请求的客户端; 反向代理,代理服务端,客户端不知道实际提供服务的服务端;

3 Nginx作用

3.1 HTTP协议代理

只要支持HTTP协议访问的内容,都可以由Nginx进行代理。Nginx只支持HTTP协议的代理,其他协议不支持。

3.2 搭建虚拟主机

Nginx可以监听所安装的主机的某个端口,对外支持这个端口的HTTP访问。当接收到外部HTTP请求后把本机中资源返回给客户端。今天的课程内容就是使用Nginx的搭建虚拟主机功能,外部请求图片时,把图片信息响应给请求发。

3.3 负载均衡

Nginx可以代理多个主机,内置负载均衡策略。

十、Nginx安装

1.上传并安装fastdfs-nginx-module

上传 /fastdfs-nginx-model_v1.16.tar.gz 到 /usr/local/tmp 中

进入 tmp 目录

# cd /usr/local/tmp


解压

# tar zxf fastdfs-nginx-module_v1.16.tar.gz


2.修改配置文件

进入解压目录中src目录

# cd fastdfs-nginx-module/src

编辑config文件

# vim config

修改配置文件中第四行,把路径中local去掉。参数是用于配置安装nginx中的FastDFS组件的时候,在什么位置查找FastDFS核心代码。

修改结果如下:

3.安装nginx的依赖

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


4.上传Nginx 并解压

上传nginx-1.16.1.tar.gz 到/usr/local/tmp中

# cd /usr/local/tmp


# tar zxf nginx-1.16.1.tar.gz


5.修改Nginx配置

5.1 进入到Nginx文件夹

# cd nginx-1.16.1


5.2 创建临时目录

修改配置文件中好多位置都使用了/var/temp/nginx目录,但是默认不会自动创建这个目录,需要手动创建。

# mkdir -p /var/temp/nginx


5.3 修改配置文件参数

./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/tmp/fastdfs-nginx-module/src

--add-module 必须定义,此配置信息是用于指定安装Nginx时需要加载的模块,如果未指定,Nginx安装过程不会加载fastdfs-nginx-module模块,后续功能无法实现。

6.编译并安装

# make
# make install


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

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

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


8 修改 mod_fastdfs.conf

8.1进入到 /etc/fdfs

# cd /etc/fdfs

8.2 编辑配置文件

# vim mod_fastdfs.conf


8.3 文件内容修改

需要修改文件中四处内容, 这四处内容的含义:

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=192.168.93.10:22122
url_have_group_name=true
store_path0=/usr/local/fastdfs/storage/store

9. 提供FastDFS需要的HTTP配置文件

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

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


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

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

# ln -s /usr/local/fastdfs/storage/store/data/ /usr/local/fastdfs/storage/store/data/M00


11. 修改nginx配置文件

11.1进入到安装后 nginx目录

注意是安装目录,不是解压目录

# cd /usr/local/nginx/conf


11.2 编辑配置文件

# vim nginx.conf


11.2.1 修改内容

需要修改两处

11.2.1.1第一处

user root; #Nginx需要访问linux文件系统,必须有文件系统的权限。User root代表nginx文件系统的权限是root用户权限。如果不开启权限,可能有404反问错误。

默认效果:此内容在文件最上面

修改后的效果:去掉注释,user后面写上root

11.2.1.2第二处

server{
listen 8888; #storage 配置中, 有http.server_post=8888的配置信息,必须一致。配置文件是 /etc/fdfs/storaged.conf
server_name localhost;
location ~/group([0-9])/M00{
ngx_fastdfs_module;
}
}

默认效果:

修改后的效果:

12. 启动nginx

进入到nginx安装目录的sbin文件夹

#cd /usr/local/nginx/sbin/

启动nginx

# ./nginx


关闭nginx

# ./nginx -s quit