主頁 >  其他 > 搭建fastdfs服務,及單機redis服務,springboot實作h5與fastdfs之間的斷點續傳,大檔案上傳,秒傳檔案和批量上傳

搭建fastdfs服務,及單機redis服務,springboot實作h5與fastdfs之間的斷點續傳,大檔案上傳,秒傳檔案和批量上傳

2021-02-07 10:38:09 其他

前言

搭建單機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,并不監聽外網的請求,

  1. 把檔案夾目錄里的redis.conf組態檔里的bind 127.0.0.1前面加#注釋掉
  2. 命令: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

標籤:其他

上一篇:什么是LVS以及搭建LVS負載均衡群集

下一篇:遠程圖傳設備學習資料保存第707篇:EMS低延時遠程圖傳設備

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more