spring boot和fastdfs是什么就不说了。百度一下你就知道。
这里我们介绍一下spring boot与fastdfs的整合使用。fastdfs怎么安装请查看我的另外一篇博客:点击打开链接。 这里假设你已经安装好了fastdfs
首先下载最新版的 fastdfs-client-java源码编译,因为这个包maven上没有,需要自己编译成jar本地安装到maven。
源码下载地址:https://github.com/happyfish100/fastdfs-client-java ,或者直接下载我已经编译好的最新版本的jar包:http://download.csdn.net/detail/kokjuis/9919803
怎么使用这个jar,这里有两种方式
1、直接在你的spring boot工程下建立lib文件夹(与src平级)。把jar包加入到lib目录,然后在pom.xml中引用:
<!-- 这个包maven上没有,用源码编译后放在本地引用 -->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/fastdfs-client-java-1.27-SNAPSHOT.jar</systemPath>
</dependency>
这样就可以在工程中引用了。不过不建议使用这种方式,因为用这种方式引用,在项目打包成jar的时候会maven会找不到这个jar。
2、把编译好的jar安装在你本地的maven库中,然后在引用:
使用maven命令安装jar包到本地。-Dfile=E:\fastdfs-client-java-1.27-SNAPSHOT.jar 路径改为你自己的路径。
mvn install:install-file -DgroupId=org.csource -DartifactId=fastdfs-client-java -Dversion=1.27-SNAPSHOT -Dpackaging=jar -Dfile=E:\fastdfs-client-java-1.27-SNAPSHOT.jar
安装完以后就能正常引用了:
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-SNAPSHOT</version>
</dependency>
然后看看怎么使用吧:
首先添加fastdfs配置文件:fdfs-client.conf 根据你自己的配置修改
# 连接tracker服务器超时时长
connect_timeout = 10
# socket连接超时时长
network_timeout = 30
# 文件内容编码
charset = UTF-8
# tracker服务器端口
http.tracker_http_port = 6666
http.anti_steal_token = no
http.secret_key = FastDFS1234567890
# tracker服务器IP和端口(可以写多个)
tracker_server = 192.168.9.181:22122
#tracker_server = xxxx:xxx
@Configuration
public class ApplicationConfig extends WebMvcConfigurerAdapter
{
static final String FASTDFS_CONFIG = "conf/fdfs-client.conf";
@Bean
public StorageClient1 initStorageClient()
{
StorageClient1 storageClient = null;
try
{
ClientGlobal.init(FASTDFS_CONFIG);
System.out.println("ClientGlobal.configInfo(): " + ClientGlobal.configInfo());
TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
TrackerServer trackerServer = trackerClient.getConnection();
if (trackerServer == null)
{
throw new IllegalStateException("getConnection return null");
}
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
if (storageServer == null)
{
throw new IllegalStateException("getStoreStorage return null");
}
storageClient = new StorageClient1(trackerServer, storageServer);
}
catch (Exception e)
{
e.printStackTrace();
}
return storageClient;
}
}
如果没有特殊要求,到这里就算配置好了,简单吧,来看看使用的示例吧:
fastdfs调用工具类:
/*
* 文件名:FastDFSClient.java 版权:Copyright by www.inhand.com 描述: 修改人:kokJuis 修改时间:2017年7月27日 跟踪单号:
* 修改单号: 修改内容:
*/
package com.inhand.fastdfs.utils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FastDFSClient
{
@Autowired
private StorageClient1 storageClient;
/**
* 上传文件
*
* @param file
* 文件对象
* @param fileName
* 文件名
* @return
*/
public String uploadFile(byte[] buff, String fileName)
{
return uploadFile(buff, fileName, null);
}
/**
* 上传文件
*
* @param file
* 文件对象
* @param fileName
* 文件名
* @param metaList
* 文件元数据
* @return
*/
public String uploadFile(byte[] buff, String fileName, Map<String, String> metaList)
{
try
{
NameValuePair[] nameValuePairs = null;
if (metaList != null)
{
nameValuePairs = new NameValuePair[metaList.size()];
int index = 0;
for (Iterator<Map.Entry<String, String>> iterator = metaList.entrySet().iterator(); iterator.hasNext();)
{
Map.Entry<String, String> entry = iterator.next();
String name = entry.getKey();
String value = entry.getValue();
nameValuePairs[index++ ] = new NameValuePair(name, value);
}
}
return storageClient.upload_file1(buff, FileUtil.getExtensionName(fileName),
nameValuePairs);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 获取文件元数据
*
* @param fileId
* 文件ID
* @return
*/
public Map<String, String> getFileMetadata(String fileId)
{
try
{
NameValuePair[] metaList = storageClient.get_metadata1(fileId);
if (metaList != null)
{
HashMap<String, String> map = new HashMap<String, String>();
for (NameValuePair metaItem : metaList)
{
map.put(metaItem.getName(), metaItem.getValue());
}
return map;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 删除文件
*
* @param fileId
* 文件ID
* @return 删除失败返回-1,否则返回0
*/
public int deleteFile(String fileId)
{
try
{
return storageClient.delete_file1(fileId);
}
catch (Exception e)
{
e.printStackTrace();
}
return -1;
}
/**
* 下载文件
*
* @param fileId
* 文件ID(上传文件成功后返回的ID)
* @return
*/
public byte[] downloadFile(String fileId)
{
try
{
byte[] content = storageClient.download_file1(fileId);
return content;
}
catch (IOException e)
{
e.printStackTrace();
}
catch (MyException e)
{
e.printStackTrace();
}
return null;
}
/**
* Description:获取文件信息
*
* @param fileId
* @return
* @see
*/
public FileInfo getFileInfo(String fileId)
{
try
{
FileInfo fileInfo = storageClient.get_file_info1(fileId);
return fileInfo;
}
catch (IOException e)
{
e.printStackTrace();
}
catch (MyException e)
{
e.printStackTrace();
}
return null;
}
}
FileUtil:
/*
* 文件名:FileUtils.java 版权:Copyright by www.huawei.com 描述: 修改人:kokJuis 修改时间:2017年7月27日 跟踪单号: 修改单号:
* 修改内容:
*/
package com.poly.fastdfs.utils;
public class FileUtil
{
/**
* Description: 获取文件后缀名
*
* @param fileName
* @return
* @see
*/
public static String getExtensionName(String fileName)
{
String prefix = fileName.substring(fileName.lastIndexOf(".") + 1);
return prefix;
}
/**
* 根据path获取文件名
*
* @author kokJuis
* @version 1.0
* @date 2016-12-12
* @param filename
* @return
*/
public static String getOriginalFilename(String filename)
{
if (filename == null) return "";
int pos = filename.lastIndexOf("/");
if (pos == -1) pos = filename.lastIndexOf("\\");
if (pos != -1)
return filename.substring(pos + 1);
else
return filename;
}
}
简单的上传下载:
package com.inhand.fastdfs.controller;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.inhand.fastdfs.base.Code;
import com.inhand.fastdfs.utils.FastDFSClient;
import com.inhand.fastdfs.utils.FileUtil;
@RestController
@RequestMapping("/file/*")
public class FileController
{
@Autowired
private FastDFSClient fastDFSClient;
/**
* 上传文件
*
* @author kokJuis
* @version 1.0
* @date 2016-12-12
* @return
*/
@RequestMapping(value = "uploadFile", method = RequestMethod.POST)
public Map<String, Object> uploadFile(@RequestParam MultipartFile filedata)
{
Map<String, Object> m = new HashMap<String, Object>();
if (filedata != null && !filedata.isEmpty())
{
try
{
String path = fastDFSClient.uploadFile(filedata.getBytes(), filedata.getOriginalFilename());
m.put("code", Code.SUCCESS);
m.put("url", path);
m.put("msg", "上传成功");
}
catch (Exception e)
{
e.printStackTrace();
m.put("code", Code.FAIL);
m.put("msg", "上传失败");
}
}
else
{
m.put("code", Code.PARAMETER_LOST);
m.put("msg", "参数丢失");
}
return m;
}
/**
* 下载文件
*
* @author kokJuis
* @version 1.0
* @date 2016-12-12
* @param imagePath
* @param local
* @return
*/
@RequestMapping(value = "getFileByPath", method = RequestMethod.GET)
public void getFileByPath(HttpServletResponse response, String path)
{
try
{
// 判断文件是否存在
if (fastDFSClient.getFileInfo(path) != null)
{
byte[] buffer = fastDFSClient.downloadFile(path);
// 清空response
response.reset();
// 设置response的Header
response.addHeader("Content-Disposition",
"attachment;filename=" + FileUtil.getOriginalFilename(path));
response.addHeader("Content-Length", "" + buffer.length);
// 通过文件流的形式写到客户端
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
// 写完以后关闭文件流
toClient.flush();
toClient.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
可以添加连接池。可以提高上传下载效率:(这里是改成spring boot后的做法,用sping的可以添加配置文件即可使用)
/*
* 文件名:ConnectionPool.java 版权:Copyright by www.huawei.com 描述: 修改人:kokJuis 修改时间:2017年8月16日 跟踪单号:
* 修改单号: 修改内容:
*/
package com.poly.fastdfs.pool;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.springframework.stereotype.Component;
@Component
public class ConnectionPool
{
private static final String FASTDFS_CONFIG = "conf/fdfs-client.conf";
/** 空闲的连接池 */
private LinkedBlockingQueue<StorageClient1> idleConnectionPool = null;
/** 空闲的忙碌连接池 */
// private ConcurrentHashMap<StorageClient1, Object> busyConnectionPool = null;
/** 连接池默认最小连接数 */
private long minPoolSize = 10;
/** 连接池默认最大连接数 */
private long maxPoolSize = 30;
/** 默认等待时间(单位:秒) */
private long waitTimes = 200;
/** fastdfs客户端创建连接默认1次 */
private static final int COUNT = 1;
private Object obj = new Object();
TrackerServer trackerServer = null;
/**
* 默认构造方法
*/
public ConnectionPool()
{
/** 初始化连接池 */
poolInit();
/** 注册心跳 */
// HeartBeat beat = new HeartBeat(this);
// beat.beat();
}
public ConnectionPool(long minPoolSize, long maxPoolSize, long waitTimes)
{
System.out.println("[线程池构造方法(ConnectionPool)][默认参数:minPoolSize=" + minPoolSize
+ ",maxPoolSize=" + maxPoolSize + ",waitTimes=" + waitTimes + "]");
this.minPoolSize = minPoolSize;
this.maxPoolSize = maxPoolSize;
this.waitTimes = waitTimes;
/** 初始化连接池 */
poolInit();
/** 注册心跳 */
HeartBeat beat = new HeartBeat(this);
beat.beat();
}
/**
* @Description: 连接池初始化 (在加载当前ConnectionPool时执行) 1).加载配置文件 2).空闲连接池初始化;
* 3).创建最小连接数的连接,并放入到空闲连接池;
*/
private void poolInit()
{
try
{
/** 加载配置文件 */
initClientGlobal();
/** 初始化空闲连接池 */
idleConnectionPool = new LinkedBlockingQueue<StorageClient1>();
/** 初始化忙碌连接池 */
// busyConnectionPool = new ConcurrentHashMap<StorageClient1, Object>();
TrackerClient trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
int flag = 0;
while (trackerServer == null && flag < 5)
{
System.out.println("[创建TrackerServer(createTrackerServer)][第" + flag + "次重建]");
flag++ ;
initClientGlobal();
trackerServer = trackerClient.getConnection();
}
// 测试 Tracker活跃情况
// ProtoCommon.activeTest(trackerServer.getSocket());
/** 往线程池中添加默认大小的线程 */
createTrackerServer();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("[FASTDFS初始化(init)--异常]");
}
}
/**
* @Description: 创建TrackerServer,并放入空闲连接池
*/
public void createTrackerServer()
{
System.out.println("[创建TrackerServer(createTrackerServer)]");
TrackerServer trackerServer = null;
try
{
for (int i = 0; i < minPoolSize; i++ )
{
// 把client1添加到连接池
StorageServer storageServer = null;
StorageClient1 client1 = new StorageClient1(trackerServer, storageServer);
idleConnectionPool.add(client1);
}
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("[创建TrackerServer(createTrackerServer)][异常:{}]");
}
}
/**
* @Description: 获取空闲连接 1).在空闲池(idleConnectionPool)中弹出一个连接; 2).把该连接放入忙碌池(busyConnectionPool)中;
* 3).返回 connection 4).如果没有idle connection, 等待 wait_time秒, and check again
* @throws AppException
*/
public StorageClient1 checkout()
{
StorageClient1 client1 = idleConnectionPool.poll();
if (client1 == null)
{
if (idleConnectionPool.size() < maxPoolSize)
{
createTrackerServer();
try
{
client1 = idleConnectionPool.poll(waitTimes, TimeUnit.SECONDS);
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("[获取空闲连接(checkout)-error][error:获取连接超时:{}]");
}
}
}
// 添加到忙碌连接池
// busyConnectionPool.put(client1, obj);
System.out.println("[获取空闲连接(checkout)][获取空闲连接成功]");
return client1;
}
/**
* @Description: 释放繁忙连接 1.如果空闲池的连接小于最小连接值,就把当前连接放入idleConnectionPool;
* 2.如果空闲池的连接等于或大于最小连接值,就把当前释放连接丢弃;
* @param client1
* 需释放的连接对象
*/
public void checkin(StorageClient1 client1)
{
System.out.println("[释放当前连接(checkin)]");
client1 = null;
if (idleConnectionPool.size() < minPoolSize)
{
createTrackerServer();
}
}
private void initClientGlobal()
throws Exception
{
ClientGlobal.init(FASTDFS_CONFIG);
}
public LinkedBlockingQueue<StorageClient1> getIdleConnectionPool()
{
return idleConnectionPool;
}
public long getMinPoolSize()
{
return minPoolSize;
}
public void setMinPoolSize(long minPoolSize)
{
if (minPoolSize != 0)
{
this.minPoolSize = minPoolSize;
}
}
public long getMaxPoolSize()
{
return maxPoolSize;
}
public void setMaxPoolSize(long maxPoolSize)
{
if (maxPoolSize != 0)
{
this.maxPoolSize = maxPoolSize;
}
}
public long getWaitTimes()
{
return waitTimes;
}
public void setWaitTimes(int waitTimes)
{
if (waitTimes != 0)
{
this.waitTimes = waitTimes;
}
}
}
使用连接池的工具类:
/*
* 文件名:FastDFSClient.java 版权:Copyright by www.inhand.com 描述: 修改人:kokJuis 修改时间:2017年7月27日 跟踪单号:
* 修改单号: 修改内容:
*/
package com.poly.fastdfs.utils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.poly.fastdfs.pool.ConnectionPool;
@Component
public class FastDFSClient
{
/** 连接池 */
@Autowired
private ConnectionPool connectionPool;
/**
* 上传文件
*
* @param file
* 文件对象
* @param fileName
* 文件名
* @return
*/
public String uploadFile(byte[] buff, String fileName)
{
return uploadFile(buff, fileName, null, null);
}
public String uploadFile(byte[] buff, String fileName, String groupName)
{
return uploadFile(buff, fileName, null, null);
}
/**
* 上传文件
*
* @param file
* 文件对象
* @param fileName
* 文件名
* @param metaList
* 文件元数据
* @return
*/
public String uploadFile(byte[] buff, String fileName, Map<String, String> metaList,
String groupName)
{
try
{
NameValuePair[] nameValuePairs = null;
if (metaList != null)
{
nameValuePairs = new NameValuePair[metaList.size()];
int index = 0;
for (Iterator<Map.Entry<String, String>> iterator = metaList.entrySet().iterator(); iterator.hasNext();)
{
Map.Entry<String, String> entry = iterator.next();
String name = entry.getKey();
String value = entry.getValue();
nameValuePairs[index++ ] = new NameValuePair(name, value);
}
}
/** 获取可用的tracker,并创建存储server */
StorageClient1 storageClient = connectionPool.checkout();
String path = null;
if (!StringUtils.isEmpty(groupName))
{
// 上传到指定分组
path = storageClient.upload_file1(groupName, buff,
FileUtil.getExtensionName(fileName), nameValuePairs);
}
else
{
path = storageClient.upload_file1(buff, FileUtil.getExtensionName(fileName),
nameValuePairs);
}
/** 上传完毕及时释放连接 */
connectionPool.checkin(storageClient);
return path;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 获取文件元数据
*
* @param fileId
* 文件ID
* @return
*/
public Map<String, String> getFileMetadata(String fileId)
{
try
{
/** 获取可用的tracker,并创建存储server */
StorageClient1 storageClient = connectionPool.checkout();
NameValuePair[] metaList = storageClient.get_metadata1(fileId);
/** 上传完毕及时释放连接 */
connectionPool.checkin(storageClient);
if (metaList != null)
{
HashMap<String, String> map = new HashMap<String, String>();
for (NameValuePair metaItem : metaList)
{
map.put(metaItem.getName(), metaItem.getValue());
}
return map;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* 删除文件
*
* @param fileId
* 文件ID
* @return 删除失败返回-1,否则返回0
*/
public int deleteFile(String fileId)
{
try
{
/** 获取可用的tracker,并创建存储server */
StorageClient1 storageClient = connectionPool.checkout();
int i = storageClient.delete_file1(fileId);
/** 上传完毕及时释放连接 */
connectionPool.checkin(storageClient);
return i;
}
catch (Exception e)
{
e.printStackTrace();
}
return -1;
}
/**
* 下载文件
*
* @param fileId
* 文件ID(上传文件成功后返回的ID)
* @return
*/
public byte[] downloadFile(String fileId)
{
try
{
/** 获取可用的tracker,并创建存储server */
StorageClient1 storageClient = connectionPool.checkout();
byte[] content = storageClient.download_file1(fileId);
/** 上传完毕及时释放连接 */
connectionPool.checkin(storageClient);
return content;
}
catch (IOException e)
{
e.printStackTrace();
}
catch (MyException e)
{
e.printStackTrace();
}
return null;
}
/**
* Description:获取文件信息
*
* @param fileId
* @return
* @see
*/
public FileInfo getFileInfo(String fileId)
{
try
{
/** 获取可用的tracker,并创建存储server */
StorageClient1 storageClient = connectionPool.checkout();
FileInfo fileInfo = storageClient.get_file_info1(fileId);
/** 上传完毕及时释放连接 */
connectionPool.checkin(storageClient);
return fileInfo;
}
catch (IOException e)
{
e.printStackTrace();
}
catch (MyException e)
{
e.printStackTrace();
}
return null;
}
}
The field file exceeds its maximum permitted size of 1048576 bytes.
或者报
Required request part 'filedata' is not present
上面的报错是因为spring boot 内置的MultipartResolver有点问题,可以这样解决:
显性注册MultipartResolver:
// 显示声明CommonsMultipartResolver为mutipartResolver
@Bean(name = "multipartResolver")
public MultipartResolver multipartResolver()
{
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
// resolver.setDefaultEncoding("UTF-8");
// resolver.setResolveLazily(true);// resolveLazily属性启用是为了推迟文件解析,以在在UploadAction中捕获文件大小异常
// resolver.setMaxInMemorySize(40960);
resolver.setMaxUploadSize(10 * 1024 * 1024);// 上传文件大小 5M 5*1024*1024
return resolver;
}
并且在spring boot的启动类添加注解:
@EnableAutoConfiguration(exclude = {MultipartAutoConfiguration.class})
这个注解的意思是排除内置的MultipartResolver。如果没有这个注解,显性注册的MultipartResolver会无效。这样处理以后就能正常上传了
全文完。。。