前言
搭建單機redis服務,結合fastdfs,springboot實作h5與fastdfs之間的斷點續傳,大檔案上傳,秒傳,技術采用:webuploader+springboot+redis+fastdfs(服務端)+FastDFS_Client,
本文所需實作工具,皆在此包中https://download.csdn.net/download/as4589sd/15086474
fastdfs服務搭建
先要安裝gcc編譯器:
yum -y install gcc-c++
下載https://download.csdn.net/download/as4589sd/15086474的工具包,進行解壓

安裝lib依賴包
tar -zxvf V1.0.43.tar.gz
cd libfastcommon-1.0.43
./make.sh
./make.sh install

如果缺少libfdfsclient.so依賴,可以下載https://download.csdn.net/download/as4589sd/15086685
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/lib64/libfastcommon.so /usr/lib/libfastcommon.so
ln -s /usr/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
ln -s /usr/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
安裝fastdfs服務
tar -zxvf V6.06.tar.gz
cd fastdfs-6.06
./make.sh
./make.sh install
配置Tracker服務
mkdir -p /data/fastdfs/tracker
cd /etc/fdfs
cp tracker.conf.sample tracker.conf
vim tracker.conf
要修改的內容為
#啟用組態檔(默認啟用)
disabled=false
#設定tracker的埠號,通常采用22122這個默認埠
port=22122
#設定tracker的資料檔案和日志目錄
base_path=/data/fastdfs/tracker
#設定http埠號,默認為8080
http.server_port=80
啟動服務
#啟動
/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start

查看是否啟動
#查看啟動埠
ss -ant | grep 22122
查看啟動日志
#查看啟動日志
tail -f /data/fastdfs/tracker/logs/trackerd.log

加入開機啟動
vim /etc/rc.d/rc.local
在加入啟動命令

如果重啟后發現未能自動啟動則通過命令
ll /etc/rc.d/rc.local
檢查一下rc.local是否具備可執行權限,若是無可執行權限則通過命令進行授權
chmod +x /etc/rc.d/rc.local
配置Storage服務
mkdir -p /data/fastdfs/storage
mkdir -p /data/fastdfs/storage/file
cd /etc/fdfs
cp storage.conf.sample storage.conf
vim storage.conf
要修改的內容為
#啟用組態檔(默認啟用)
disabled=false
#組名,根據實際情況修改
group_name=group1
#設定storage的埠號,默認是23000,同一個組的storage埠號必須一致
port=23000
#設定storage資料檔案和日志目錄
base_path=/data/fastdfs/storage
#存盤路徑個數,需要和store_path個數匹配
store_path_count=1
#實際檔案存盤路徑
store_path0=/data/fastdfs/storage/file
#tracker 服務器的 IP地址和埠號,如果是單機搭建,IP不要寫127.0.0.1,否則啟動不成功(此處的ip是我的CentOS虛擬機ip)
tracker_server=172.16.6.50:22122
#設定 http 埠號
http.server_port=8888
啟動服務
#啟動
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
查看啟動埠
#查看啟動埠
ss -ant | grep 23000
查看啟動日志
#查看啟動日志
tail -f /data/fastdfs/storage/logs/storaged.log

通過monitor查看storage是否系結成功
[root@localhost /]# /usr/bin/fdfs_monitor /etc/fdfs/storage.conf
[2021-09-23 12:59:26] DEBUG - base_path=/opt/fastdfs_storage, connect_timeout=30, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0
server_count=1, server_index=0
tracker server is 172.16.8.11:22122
group count: 1
Group 1:
group name = group1
disk total space = 6818 MB
disk free space = 2169 MB
trunk free space = 0 MB
……
加入開機啟動
vim /etc/rc.d/rc.local
在該檔案中,加入啟動命令
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
測驗驗證
[root@localhost ~]# ps -ef|grep fdfs
root 10335 17685 0 23:50 pts/3 00:00:00 grep --color=auto fdfs
root 13335 1 0 13:17 ? 00:00:07 /usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
root 15779 1 0 12:59 ? 00:00:04 /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf
用fdfs客戶端進行測驗
cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
測驗時需要設定客戶端的組態檔
vim /etc/fdfs/client.conf
打開檔案后依次做以下修改:
#tracker服務器檔案路徑
base_path=/data/fastdfs/tracker
#tracker服務器IP地址和埠號
tracker_server=172.16.7.50:22122
# tracker 服務器的 http 埠號,必須和tracker的設定對應起來
http.tracker_server_port=80
配置完成后就可以模擬檔案上傳了,先給/data目錄下放一檔案test.txt,然后通過執行客戶端上傳命令嘗試上傳:
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /data/test.txt
單機redis服務搭建
1.獲取redis資源,并解壓
從https://download.csdn.net/download/as4589sd/15086474的工具包中,解壓redis-4.0.8.tar.gz
tar xzvf redis-4.0.8.tar.gz
2.安裝
cd redis-4.0.8
make
cd src
make install PREFIX=/usr/local/redis
3.移動組態檔到安裝目錄下
cd ../
mkdir /usr/local/redis/etc
mv redis.conf /usr/local/redis/etc
4.配置redis為后臺啟動
vim /usr/local/redis/etc/redis.conf
注意,將daemonize no 改成daemonize yes,及注釋掉bind 127.0.0.1,可以遠程訪問
5.將redis加入到開機啟動
vim /etc/rc.d/rc.local
在里面添加內容:
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
意思就是開機呼叫這段開啟redis的命令.
6.啟動redis
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
7.將redis-cli,redis-server拷貝到bin下,讓redis-cli指令可以在任意目錄下直接使用
cp /usr/local/redis/bin/redis-server /usr/local/bin/
cp /usr/local/redis/bin/redis-cli /usr/local/bin/
8.設定redis密碼
a.運行命令:
redis-cli
b.查看現有的redis密碼(可選操作,可以沒有)
運行命令:
config get requirepass
如果沒有設定過密碼的話運行結果會如下圖所示

c.設定redis密碼
運行命令:
config set requirepass *******
(******為你要設定的密碼),設定成功的話會回傳‘OK’字樣
d.測驗連接
重啟redis服務
//****為你設定的密碼
redis-cli -h 127.0.0.1 -p 6379 -a ****
也可以,輸入 redis-cli 進入命令模式,使用 auth '*****' (****為你設定的密碼)登陸
9.讓外網能夠訪問redis
a.配置防火墻:
#開放6379埠
firewall-cmd --zone=public --add-port=6379/tcp --permanent
#重啟防火墻以使配置即時生效
systemctl restart firewalld
#查看系統所有開放的埠
firewall-cmd --zone=public --list-ports
b.若是仍不可遠程訪問:
此時 雖然防火墻開放了6379埠,但是外網還是無法訪問的,因為redis監聽的是127.0.0.1:6379,并不監聽外網的請求,
- 把檔案夾目錄里的redis.conf組態檔里的bind 127.0.0.1前面加#注釋掉
- 命令:redis-cli連接到redis后,通過 config get daemonize和config get protected-mode 是不是都為no,如果不是,就用config set 配置名 屬性 改為no,
其他redis相關常用命令
卸載redis:
#停止redis
pkill redis
#洗掉安裝目錄
rm -rf /usr/local/redis
#洗掉所有redis相關命令腳本
rm -rf /usr/bin/redis-*
#洗掉redis解壓檔案夾
rm -rf /root/download/redis-4.0.4
報例外,嘗試重啟
原因: Redis已經啟動
解決: 關掉Redis,重啟即可
redis-cli shutdown
redis-server &
然后你就能看到Redis愉快的運行了.
客戶端檢測連接是否正常
使用redis-cli客戶端檢測連接是否正常
$redis-cli
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set key "hello world"
OK
127.0.0.1:6379> get key
"hello world"
Redis客戶端常見操作
Redis是key-value資料庫,支持五種資料型別:string(字串),hash(哈希),list(串列),set(集合)及zset(sorted set:有序集合),
當value是string型別,命令包括set get setnx incr del 等,
> set server:name "fido" // 設定鍵值
OK
> get server:name // 獲取鍵值
"fido"
> setnx connections 10 // set if not exists
OK
> incr connections // 原子性增加values值
(integer) 11
> incr connections
(integer) 12
> del connections // 洗掉key
(integer) 1
> incr connections
(integer) 1
當value是list型別,命令包括rpush lpush llen lrange lpop rpop del 等,
> rpush friends "Alice" // 在末尾追加
(integer) 1
> rpush friends "Bob"
(integer) 2
> lpush friends "Sam" // 插入到開頭
(integer) 3
> lrange friends 0 -1 // 回傳串列的子集,類似切片操作
1) "Sam"
2) "Alice"
3) "Bob"
> lrange friends 0 1
1) "Sam"
2) "Alice"
> lrange friends 1 2
1) "Alice"
2) "Bob"
> llen friends // 回傳串列長度
(integer) 3
> lpop friends // 洗掉并回傳串列第一個元素
"Sam"
> rpop friends // 洗掉并回傳串列最后一個元素
"Bob"
> lrange friends 0 -1
1) "Alice"
> del friends // 洗掉key
(integer) 1 // 1表示成功,0表示失敗
當value是set型別,命令包括sadd srem sismember smembers sunion del等,
> sadd superpowers "flight" // 添加元素
(integer) 1
> sadd superpowers "x-ray vision"
(integer) 1
> sadd superpowers "reflexes"
(integer) 1
> srem superpowers "reflexes" // 洗掉元素1
> sismember superpowers "flight" // 測驗元素是否在集合中
(integer) 1
> sismember superpowers "reflexes"
(integer) 0
> smembers superpowers // 回傳集合中所有元素
1) "x-ray vision"
2) "flight"
> sadd birdpowers "pecking"
(integer) 1
> sadd birdpowers "flight"
(integer) 1
> sunion superpowers birdpowers // 合并多個set,回傳合并后的元素串列
1) "x-ray vision"
2) "flight"
3) "pecking"
> del superpowers // 洗掉key
(integer) 1
當value是zset型別,命令包括 zadd zrange del等,注意給value一個編號用于排序,
> zadd hacker 1940 "Alan Kay" // 給value指定一個編號,比如以年份1940作為編號
(integer) 1
> zadd hacker 1906 "Grace Hopper"
(integer) 1
> zadd hacker 1953 "Richard Stallman"
(integer) 1
> zadd hacker 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hacker 1916 "Claude Shannon"
(integer) 1
> zadd hacker 1969 "Linux Torvalds"
(integer) 1
> zadd hacker 1957 "Sophie Wilson"
(integer) 1
> zadd hacker 1912 "Alan Turing"
(integer) 1
> zrange hacker 2 4 // 切片回傳有序集合中元素
1) "Claude Shannon"
2) "Alan Kay"
3) "Richard Stallman"
> del hacker // 洗掉key
(integer) 1
當value是hash型別,hash型別可以理解為字典,需要給value指定一個field用于映射,命令包括hset hmset hget hgetall hdel hincrby del 等,
> hset user:1000 name "John Smith" // 給value指定一個field,比如name
(integer) 1
> hset user:1000 email "john.smith@example.com"
(integer) 1
> hset user:1000 password "s3cret"
(integer) 1
> hgetall user:1000 // 獲得hash表中所有成員,包括field和value
1) "name"
2) "John Smith"
3) "email"
4) "john.smith@example.com"
5) "password"
6) "s3cret"
> hmset user:1001 name "Mary Jones" password "hidden" email
"mjones@example.com" // 設定多個field和value
OK
> hget user:1001 name // 根據field獲取value
"Mary Jones"
> hset user:1000 visits 10 // field可以映射到數字值
(integer) 1
> hincrby user:1000 visits 1 // 原子性增加value的值,增加1
(integer) 11
> hincrby user:1000 visits 10 // 增加10
(integer) 21
> hdel user:1000 visits // 洗掉field及其value
(integer) 1
> hincrby user:1000 visits 1
(integer) 1
> del user:1000 // 洗掉key
(integer) 1
設定和查看key的生命周期,key過期會被自動洗掉,命令包括expire ttl 等,
> set resource:lock "Redis Demo"
OK
> expire resource:lock 120 // 設定生命周期為120s
(integer) 1
> ttl resource:lock // 查看當前生命周期還剩多少時間
(integer) 109
> ttl resource:lock // 120s后查看,回傳-2表示已過期或不存在
(integer) -2
> set resource:lock "Redis Demo 2"
OK
> ttl resource:lock // 回傳-1表示永不過期
(integer) -1
springboot實作h5與fastdfs之間的斷點續傳,大檔案上傳,秒傳檔案和批量上傳
源代碼:https://gitee.com/cosmosLearn/renewFastdfs.git
代碼比較簡單明了,完全詮釋面向物件的思想……
對比,只是單純回傳一個String型別?

改為upload_list8888,頁面呼叫是失敗的

改回upload_list

ThreadLocal與redis秒配,解決了redis執行緒池在被共享訪問時帶來的執行緒安全問題
打個比方,現在公司所有人都要填寫一個表格,但是只有一支筆,這個時候就只能上個人用完了之后,下個人才可以使用,為了保證"筆"這個資源的可用性,只需要保證在接下來每個人的獲取順序就可以了,這就是 lock 的作用,當這支筆被別人用的時候,我就加 lock ,你來了那就進入佇列排隊等待獲取資源(非公平方式那就另外說了),這支筆用完之后就釋放 lock ,然后按照順序給下個人使用,
但是完全可以一個人一支筆對不對,這樣的話,你填寫你的表格,我填寫我的表格,咱倆誰都不耽擱誰,這就是 ThreadLocal 在做的事情,因為每個 Thread 都有一個副本,就不存在資源競爭,所以也就不需要加鎖,這不就是拿空間去換了時間嘛!
了解決redis執行緒池物件(筆),不能被多執行緒(多個人)共享訪問的問題,通過 threadLocal.set() 方法,將redis執行緒池物件實體保存在每個執行緒,自己所擁有的 threadLocalMap中(生成多個副本),
這樣的話,每個執行緒都使用自己的redis執行緒池物件實體,彼此不會影響,從而達到了隔離的作用,這樣就解決了redis執行緒池物件在被共享訪問時帶來的執行緒安全問題,

import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.List;
public class RedisUtil {
private RedisUtil() {
}
private static Logger _logger = LoggerFactory.getLogger(RedisUtil.class);
;
protected static final ThreadLocal<Jedis> threadLocalJedis = new ThreadLocal<Jedis>();
//Redis服務器IP
private static String ADDR_ARRAY = ReadProper.getResourceValue("spring.redis.host");
//Redis的埠號
private static int PORT = Integer.parseInt(ReadProper.getResourceValue("spring.redis.port"));
//訪問密碼
private static String AUTH = ReadProper.getResourceValue("spring.redis.password");
//可用連接實體的最大數目,默認值為8;
//如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis實體,則此時pool的狀態為exhausted(耗盡),
private static int MAX_ACTIVE = -1;
//控制一個pool最多有多少個狀態為idle(空閑的)的jedis實體,默認值也是8,
private static int MAX_IDLE = 16;
//等待可用連接的最大時間,單位毫秒,默認值為-1,表示永不超時,如果超過等待時間,則直接拋出JedisConnectionException;
private static int MAX_WAIT = 1000 * 5;
//超時時間
private static int TIMEOUT = 1000 * 5;
//在borrow一個jedis實體時,是否提前進行validate操作;如果為true,則得到的jedis實體均是可用的;
private static boolean TEST_ON_BORROW = true;
private static JedisPool jedisPool ;
//默認的資料庫為0
/**
* redis過期時間,以秒為單位
*/
public final static int EXRP_HOUR = 60 * 60; //一小時
public final static int EXRP_DAY = 60 * 60 * 24; //一天
public final static int EXRP_MONTH = 60 * 60 * 24 * 30; //一個月
/**
* 初始化Redis連接池,注意一定要在使用前初始化一次,一般在專案啟動時初始化就行了
*/
public static JedisPool initialPool() {
JedisPool jp=null;
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
config.setTestOnCreate(true);
config.setTestWhileIdle(true);
config.setTestOnReturn(true);
config.setNumTestsPerEvictionRun(-1);
jp = new JedisPool(config, ADDR_ARRAY, PORT, TIMEOUT, AUTH);
jedisPool=jp;
threadLocalJedis.set(getJedis());
} catch (Exception e) {
e.printStackTrace();
_logger.error("redis服務器例外",e);
}
return jp;
}
public static void close(Jedis jedis) {
if (threadLocalJedis.get() == null && jedis != null){
jedis.close();
}
}
// /**
// * 在多執行緒環境同步初始化
// */
// private static synchronized void poolInit() {
// if (jedisPool == null) {
// initialPool();
// }
// }
/**
* 獲取Jedis實體,一定先初始化
*
* @return Jedis
*/
public static Jedis getJedis() {
boolean success = false;
Jedis jedis = null;
// if (jedisPool == null) {
// poolInit();
// }
int i=0;
while (!success) {
i++;
try {
if (jedisPool != null) {
jedis=threadLocalJedis.get();
if (jedis==null){
jedis = jedisPool.getResource();
}else {
if(! jedis.isConnected()&&!jedis.getClient().isBroken()){
threadLocalJedis.set(null);
jedis = jedisPool.getResource();
}
//System.out.println(Thread.currentThread().getName()+":第"+i+"次獲取成功#@利用了本地快取redis");
return jedis;
}
}else {
throw new RuntimeException("redis連接池初始化失敗");
}
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+":第"+i+"次獲取失敗!!!");
success = false;
e.printStackTrace();
_logger.error("redis服務器例外",e);
}
if (jedis!=null){
success=true;
}
if (i>=10&&i<20){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i>=20&&i<30){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i>=30&&i<40){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i>=40){
System.out.println("redis徹底連不上了~~~~(>_<)~~~~");
return null;
}
}
if (threadLocalJedis.get()==null){threadLocalJedis.set(jedis);}
//System.out.println(Thread.currentThread().getName()+":第"+i+"次獲取成功@");
return jedis;
}
/**
* 設定 String
*
* @param key
* @param value
*/
public static void setString(String key, String value) {
Jedis jo = null;
try {
value = StrUtil.isBlank(value) ? "" : value;
jo = getJedis();
jo.set(key, value);
} catch (Exception e) {
threadLocalJedis.set(null);
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis服務器例外");
} finally {
if (jo != null) {
close(jo);
}
}
}
/**
* 設定 過期時間
*
* @param key
* @param seconds 以秒為單位
* @param value
*/
public static void setString(String key, int seconds, String value) {
Jedis jo = null;
try {
value = StrUtil.isBlank(value) ? "" : value;
jo = getJedis();
jo.setex(key, seconds, value);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis服務器例外");
} finally {
if (jo != null) {
close(jo);
}
}
}
/**
* 獲取String值
*
* @param key
* @return value
*/
public static String getString(String key) {
Jedis jo = null;
try {
jo = getJedis();
if (jo == null || !jo.exists(key)) {
return null;
}
return jo.get(key);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
public static long incrBy(String key, long integer) {
Jedis jo = null;
try {
jo = getJedis();
return jo.incrBy(key, integer);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
public static long decrBy(String key, long integer) {
Jedis jo = null;
try {
jo = getJedis();
return jo.decrBy(key, integer);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
//洗掉多個key
public static long delKeys(String [] keys){
Jedis jo = null;
try {
jo = getJedis();
return jo.del(keys);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
//洗掉單個key
public static long delKey(String key){
Jedis jo = null;
try {
jo = getJedis();
return jo.del(key);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
//添加到佇列尾
public static long rpush(String key,String node){
Jedis jo = null;
try {
jo = getJedis();
return jo.rpush(key,node);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
//洗掉list元素
public static long delListNode(String key,int count,String value){
Jedis jo = null;
try {
jo = getJedis();
return jo.lrem(key,count,value);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
}
//獲取所有list
public static List getListAll(String key){
Jedis jo = null;
List list=null;
try {
jo = getJedis();
list= jo.lrange(key,0,-1);
} catch (Exception e) {
threadLocalJedis.set(null);
e.printStackTrace();
_logger.error("redis服務器例外",e);
throw new RuntimeException("redis操作錯誤");
} finally {
if (jo != null) {
close(jo);
}
}
return list;
}
//清理快取redis
public static void cleanLoacl(Jedis jo){
threadLocalJedis.set(null);
close(jo);
}
static {
initialPool();
}
}
使用webuploader組件實作大檔案分片上傳,斷點續傳
webuploader:是一個以HTML5為主, Flash為輔的檔案上傳組件,采用大檔案分片/并發上傳的方式,極大地提高了檔案上傳的效率,同時兼容多種瀏覽器版本;
前端

引入百度Webuploader組件,需要注意標簽的id/nama屬性,這些將在后面的JavaScript中使用到進行檔案切分、驗證,

以上js組件,將完成檔案上傳、MD5驗證、洗掉、切片、上傳進度條顯示、暫停、繼續上傳及上傳成功/失敗時候的回呼,
后端
前端,給后端提供封裝的chunk,及request

后端,主要是判斷檔案是否有分片,如果沒有,則直接存放到目的目錄;
如果存在分片,則創建臨時目錄,存放分片資訊;
之后判斷當前分片所屬的檔案的所有分片是否已經傳輸完畢,如果當前分片數==所屬檔案總分片數,則開始合并檔案并轉移完整檔案到目的目錄,并且洗掉臨時目錄

檢測完檔案,以后,開始上傳操作
//上傳操作
path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP, file.getInputStream(),file.getSize(), FileUtil.extName((String) paramMap.get("name")));
//更新操作
appendFileStorageClient.modifyFile(UpLoadConstant.DEFAULT_GROUP, noGroupPath, file.getInputStream(),file.getSize(),historyUpload);
hutool工具的巧妙運用
可參考hutool資料http://www.mianshigee.com/tutorial/hutool/26e24c7a37d93249.md
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.0.6</version>
</dependency>
cn.hutool.core.collection.CollUtil判斷非空

cn.hutool.json.JSONObject與JSONUtil
if (CollUtil.isNotEmpty(fileList)){
for (String e:fileList){
JSONObject jsonObject= JSONUtil.parseObj(e);
jsonObjects.add(jsonObject);
}
}
cn.hutool.core.convert.Convert型別轉換

cn.hutool.core.util.RandomUtil生成隨機字串及StrUtil判斷字符為空

決議檔案的擴展名,來獲該檔案的型別
import cn.hutool.core.io.FileUtil;
FileUtil.extName((String) paramMap.get("name")));
總結
整體搭建整個環境,還是比較順利的,關鍵是學習該開源代碼中,springboot如何實作h5與fastdfs之間的斷點續傳,大檔案上傳,秒傳檔案和批量上傳,
前端代碼引入了百度Webuploader組件,實作檔案上傳、MD5驗證、洗掉、切片、上傳進度條顯示、暫停、繼續上傳及上傳成功/失敗時候的回呼,后端,主利用redis工具,進行判斷檔案是否有分片,如果沒有,則直接存放到fastdfs目錄;如果存在分片,則創建臨時目錄,存放分片資訊;之后判斷當前分片所屬的檔案的所有分片是否已經傳輸完畢,如果當前分片數==所屬檔案總分片數,則開始合并檔案并轉移完整檔案到fastdfs目錄,并且洗掉臨時目錄,
感興趣的同學,可以自己搭建debug一下,超有營養,有什么問題,可以私信一起交流,探討!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/257384.html
標籤:其他
