因为项目需要搭建文件服务器,研究后,最后选型FastDFS分布式文件存储系统。
由于docker镜像安装的方便化,所以决定采用docker安装。
docker pull morunchang/fastdfs
[root@iZh3cshm0xz7wjZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/morunchang/fastdfs latest a729ac95698a 18 months ago 460.1 MB
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
docker run -d --name storage --net=host -e TRACKER_IP=:22122 -e GROUP_NAME= morunchang/fastdfs sh storage.sh
/*
1.使用的网络模式是–net=host, 替换为你机器的Ip即可
2. 是组名,即storage的组,下面默认用group1
3.如果想要增加新的storage服务器,再次运行该命令,注意更换 新组名
4. docker ps 查看容器信息
[root@iZh3cshm0xz7wjZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ccdf6bbeab48 morunchang/fastdfs "sh storage.sh" 5 seconds ago Up 4 seconds storage
a7253c93bce1 morunchang/fastdfs "sh tracker.sh" About a minute ago Up About a minute tracker
*/
进入storage的容器内部,修改nginx.conf
//1.进入容器内部
docker exec -it storage /bin/bash
// storage 是 docker ps 中的NAMES
// exit 退出
root@iZh3cshm0xz7wjZ:/# cd data
root@iZh3cshm0xz7wjZ:/data# ls
fast_data fastdfs fastdfs-nginx-module libfastcommon nginx nginx-1.9.11.tar.gz
//2.修改nginx配置文件
root@iZh3cshm0xz7wjZ:/# vi /data/nginx/conf/nginx.conf
//3. 添加修改内容
location /group1/M00 {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key $uri$is_args$args;
proxy_pass http://fdfs_group1;
expires 30d;
}
//4.退出
root@iZh3cshm0xz7wjZ:/data/nginx/conf# exit
exit
//5. 重启storage服务
[root@iZh3cshm0xz7wjZ ~]# docker restart storage
storage
到这里docker已经安装完成。
---------------------------------------华丽的分割线-----------------------------------------------
下面是与springMVC项目集成
上传fastdfs-client-java至nexus私有库上,
配置项目maven的pom文件
org.csource
fastdfs-client-java
1.27
resources下新建fast_client.conf文件
并新增以下内容
connect_timeout = 60
#网络超时时间
network_timeout = 60
#字符集
charset = UTF-8
#跟踪服务器的端口
http.tracker_http_port = 8080
http.anti_steal_token = no
http.secret_key = 123456
#跟踪服务器地址 。跟踪服务器主要是起到负载均衡的作用
tracker_server = ip:22122
新增工具类FastDFSUtil.java和FileUtil.java
package com.ls.common.web.utils.fastdfs;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* FastDFS工具类
* @author yusheng
* @since 2018-4-28
* @version 1.0
*/
public class FastDFSUtil {
private final static
Logger logger = LoggerFactory.getLogger(FastDFSUtil.class);
/**
*上传服务器本地文件-通过Linux客户端,调用客户端命令上传
* @param filePath 文件绝对路径
* @return Map
*/
public static Map
Map
/**
* 1.上传文件的命令
*/
String command = "fdfs_upload_file /etc/fdfs/client.conf " + filePath;
/**
* 2.定义文件的返回信息
*/
String fileId = "";
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
/**
* 3.通过调用api, 执行linux命令上传文件
*/
Process process = Runtime.getRuntime().exec(command);
/**
* 4.读取上传后返回的信息
*/
inputStreamReader = new InputStreamReader(process.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader);
String line;
if ((line = bufferedReader.readLine()) != null) {
fileId = line;
}
/**
* 5.如果fileId包含M00,说明文件已经上传成功。否则文件上传失败
*/
if (fileId.contains("M00")) {
retMap.put("code", "0000");
retMap.put("group", fileId.substring(0, 6));
retMap.put("msg", fileId.substring(7, fileId.length()));
} else {
retMap.put("code", "0001"); //上传错误
retMap.put("msg", fileId); //返回信息
}
} catch (Exception e) {
logger.error("IOException:" + e.getMessage());
retMap.put("code", "0002");
retMap.put("msg", e.getMessage());
}finally {
if (inputStreamReader!=null){
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return retMap;
}
/**
* Description: 直接通过fdfs java客户端上传到服务器-读取本地文件上传
*
* @param filePath 本地文件绝对路径
* @return Map
*/
public static Map
Map
File file = new File(filePath);
TrackerServer trackerServer = null;
StorageServer storageServer = null;
if (file.isFile()) {
try {
String tempFileName = file.getName();
byte[] fileBuff = FileUtil.getBytesFromFile(file);
String fileId = "";
//截取后缀
String fileExtName = tempFileName.substring(tempFileName.lastIndexOf(".") + 1);
ConfigAndConnectionServer configAndConnectionServer = new ConfigAndConnectionServer().invoke(1);
StorageClient1 storageClient1 = configAndConnectionServer.getStorageClient1();
storageServer = configAndConnectionServer.getStorageServer();
trackerServer = configAndConnectionServer.getTrackerServer();
/**
* 4.设置文件的相关属性。调用客户端的upload_file1的方法上传文件
*/
NameValuePair[] metaList = new NameValuePair[3];
//原始文件名称
metaList[0] = new NameValuePair("fileName", tempFileName);
//文件后缀
metaList[1] = new NameValuePair("fileExtName", fileExtName);
//文件大小
metaList[2] = new NameValuePair("fileLength", String.valueOf(file.length()));
//开始上传文件
fileId = storageClient1.upload_file1(fileBuff, fileExtName, metaList);
retMap = handleResult(retMap, fileId);
} catch (Exception e) {
e.printStackTrace();
retMap.put("code", "0002");
retMap.put("msg", e.getMessage());
} finally {
/**
* 5.关闭跟踪服务器的连接
*/
colse(storageServer, trackerServer);
}
} else {
retMap.put("code", "0001");
retMap.put("msg", "error:本地文件不存在!");
}
return retMap;
}
/**
* Description:远程选择上传文件-通过MultipartFile
*
* @param file 文件流
* @return Map
*/
public static Map
Map
TrackerServer trackerServer = null;
StorageServer storageServer = null;
try {
if (file.isEmpty()) {
retMap.put("code", "0001");
retMap.put("msg", "error:文件为空!");
} else {
ConfigAndConnectionServer configAndConnectionServer = new ConfigAndConnectionServer().invoke(1);
StorageClient1 storageClient1 = configAndConnectionServer.getStorageClient1();
storageServer = configAndConnectionServer.getStorageServer();
trackerServer = configAndConnectionServer.getTrackerServer();
String tempFileName = file.getOriginalFilename();
//设置元信息
NameValuePair[] metaList = new NameValuePair[3];
//原始文件名称
metaList[0] = new NameValuePair("fileName", tempFileName);
//文件后缀
byte[] fileBuff = file.getBytes();
String fileId = "";
//截取后缀
String fileExtName = tempFileName.substring(tempFileName.lastIndexOf(".") + 1);
metaList[1] = new NameValuePair("fileExtName", fileExtName);
//文件大小
metaList[2] = new NameValuePair("fileLength", String.valueOf(file.getSize()));
/**
* 4.调用客户端呢的upload_file1的方法开始上传文件
*/
fileId = storageClient1.upload_file1(fileBuff, fileExtName, metaList);
retMap = handleResult(retMap, fileId);
}
} catch (Exception e) {
e.printStackTrace();
retMap.put("code", "0002");
retMap.put("msg", "error:文件上传失败!");
}finally {
/**
* 5.关闭跟踪服务器的连接
*/
colse(storageServer, trackerServer);
}
return retMap;
}
/**
* 下载文件
*
* @param response
* @param filepath 数据库存的文件路径
* @param downname 下载后的名称
* filepath M00/开头的文件路径
* group 文件所在的组 如:group0
* @throws IOException
*/
public static void download(HttpServletResponse response, String group, String filepath, String downname) {
StorageServer storageServer = null;
TrackerServer trackerServer = null;
try {
ConfigAndConnectionServer configAndConnectionServer = new ConfigAndConnectionServer().invoke(0);
StorageClient storageClient = configAndConnectionServer.getStorageClient();
storageServer = configAndConnectionServer.getStorageServer();
trackerServer = configAndConnectionServer.getTrackerServer();
/**
*4.调用客户端的下载download_file的方法
*/
byte[] b = storageClient.download_file(group, filepath);
if (b == null) {
logger.error("Error1 : file not Found!");
response.getWriter().write("Error1 : file not Found!");
} else {
logger.info("下载文件..");
downname = new String(downname.getBytes("utf-8"), "ISO8859-1");
response.setHeader("Content-Disposition", "attachment;fileName=" + downname);
OutputStream out = response.getOutputStream();
out.write(b);
out.close();
}
} catch (Exception e) {
e.printStackTrace();
try {
response.getWriter().write("Error1 : file not Found!");
} catch (IOException e1) {
e1.printStackTrace();
}
}finally {
/**
* 5.关闭跟踪服务器的连接
*/
colse(storageServer, trackerServer);
}
}
/**
* 删除文件
*
* @param group 文件分组, filepath 已M00/ 开头的文件路径
* @return Map
*/
public static Map
Map
StorageServer storageServer = null;
TrackerServer trackerServer = null;
try {
ConfigAndConnectionServer configAndConnectionServer = new ConfigAndConnectionServer().invoke(0);
StorageClient storageClient = configAndConnectionServer.getStorageClient();
storageServer = configAndConnectionServer.getStorageServer();
trackerServer = configAndConnectionServer.getTrackerServer();
/**
* 4.调用客户端的delete_file方法删除文件
*/
int i = storageClient.delete_file(group, filepath);
if (i == 0) {
retMap.put("code", "0000");
retMap.put("msg", "删除成功!");
} else {
retMap.put("code", "0001");
retMap.put("msg", "文件不存在!");
}
} catch (Exception e) {
e.printStackTrace();
retMap.put("code", "0002");
retMap.put("msg", "删除失败!");
} finally {
/**
* 5.关闭跟踪服务器的连接
*/
colse(storageServer, trackerServer);
}
return retMap;
}
/**
* 关闭服务器
*
* @param storageServer
* @param trackerServer
*/
private static void colse(StorageServer storageServer, TrackerServer trackerServer) {
if (storageServer != null && trackerServer != null) {
try {
storageServer.close();
trackerServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 处理上传到文件服务器之后,返回来的结果
*
* @param retMap
* @param fileId
* @return
*/
private static Map
if (!fileId.equals("") && fileId != null) {
retMap.put("code", "0000");
retMap.put("group", fileId.substring(0, 6));
retMap.put("msg", fileId.substring(7, fileId.length()));
} else {
retMap.put("code", "0003");
retMap.put("msg", "error:上传失败!");
}
return retMap;
}
/**
* FastDFS客户端配置
*/
private static class ConfigAndConnectionServer {
private TrackerServer trackerServer;
private StorageServer storageServer;
private StorageClient storageClient;
private StorageClient1 storageClient1;
public TrackerServer getTrackerServer() {
return trackerServer;
}
public StorageServer getStorageServer() {
return storageServer;
}
public StorageClient getStorageClient() {
return storageClient;
}
public StorageClient1 getStorageClient1() {
return storageClient1;
}
public ConfigAndConnectionServer invoke(int flag) throws IOException, MyException {
/**
* 1.读取fastDFS客户端配置文件
*/
ClassPathResource cpr = new ClassPathResource("fdfs_client.conf");
/**
* 2.配置文件的初始化信息
*/
ClientGlobal.init(cpr.getClassLoader().getResource("fdfs_client.conf").getPath());
TrackerClient tracker = new TrackerClient();
/**
* 3.建立连接
*/
trackerServer = tracker.getConnection();
storageServer = null;
/**
* 如果flag=0时候,构造StorageClient对象否则构造StorageClient1
*/
if (flag == 0) {
storageClient = new StorageClient(trackerServer, storageServer);
} else {
storageClient1 = new StorageClient1(trackerServer, storageServer);
}
return this;
}
}
}
package com.ls.common.web.utils.fastdfs;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import java.io.*;
import java.util.Date;
/**
* FastDFS工具类
* @author yusheng
* @since 2018-4-28
* @version 1.0
*/
public class FileUtil {
public static void saveImage(CommonsMultipartFile[] files, int i) {
if(!files[i].isEmpty()){
int pre = (int) System.currentTimeMillis();
try {
//拿到输出流,同时重命名上传的文件
FileOutputStream os = new FileOutputStream("f:/img"+"/" + new Date().getTime()+".jpg");
//拿到上传文件的输入流
ByteArrayInputStream in = (ByteArrayInputStream) files[i].getInputStream();
//以写字节的方式写文件
int b = 0;
while((b=in.read()) != -1){
os.write(b);
}
os.flush();
os.close();
in.close();
int finaltime = (int) System.currentTimeMillis();
System.out.println(finaltime - pre);
} catch (Exception e) {
e.printStackTrace();
System.out.println("上传出错");
}
}
}
/**
* 获取文件流
* @param f
* @return
*/
public static byte[] getBytesFromFile(File f){
if (f == null) {
return null;
}
try {
FileInputStream stream = new FileInputStream(f);
ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
for (int n;(n = stream.read(b)) != -1;) {
out.write(b, 0, n);
}
stream.close();
out.close();
return out.toByteArray();
} catch (IOException e) {
}
return null;
}
}
编写测试类
@RequestMapping("/upload")
public String upload(@RequestParam("file") CommonsMultipartFile[] files,
HttpServletRequest request){
for(int i = 0;i retMap = FastDFSUtil.upload(files[i]);
String code = (String) retMap.get("code");
String group = (String) retMap.get("group");
String msg = (String) retMap.get("msg");
if ("0000".equals(code)){
logger.info("文件上传成功");
//TODO:将上传文件的路径保存到mysql数据库
}else {
logger.info("文件上传失败");
}
}
return "/success";
}
返回msg为文件存储路径,测试后拼接ip端口,浏览器访问,可以访问则集成成功。