用asp.net core結合fastdfs打造分佈式文件存儲系統

今天主要是對開發過程,以及對FastDFS這個通用的分佈式文件存儲服務的單機及集群安裝部署過程做個總結。希望對想要自建分佈式文件系統的朋友有所幫助。

什麼是FastDFS

這裡先簡單介紹下分佈式文件存儲系統。FastDFS 是一個開源的高性能分佈式文件系統(DFS)。 它的主要功能包括:文件存儲,文件同步和文件訪問,以及高容量和負載平衡。主要解決了海量數據存儲問題,特別適合以中小文件(建議範圍:4KB < file_size <500MB>  Tracker Server:跟蹤服務器,主要做調度工作,起到均衡的作用;負責管理所有的 storage server和 group,每個 storage 在啟動後會連接 Tracker,告知自己所屬 group 等信息,並保持週期性心跳。  Storage Server:存儲服務器,主要提供容量和備份服務;以 group 為單位,每個 group 內可以有多臺 storage server,數據互為備份。  Client:客戶端,上傳下載數據的服務器,也就是我們自己的項目所部署在的服務器。

用asp.net core結合fastdfs打造分佈式文件存儲系統

FastDFS開源地址:https://github.com/happyfish100

FastDFS的單機版安裝

這裡先簡單介紹下單機版的安裝,跟著做你也可以從0還是搭建一個單機版的FastDFS。集群版也是在單機版基礎上安裝的。

第一步 安裝相關工具

如果已經安裝和wget可以跳過這一步

<code>

yum

-y install vim wget unzip/<code>

-y:不經過詢問直接同意所有操作vim:linux上面一個比較好用的文本編輯軟件wget:linux上文件下載工具

第二步 安裝相關依賴

<code>

yum

install -y gcc-c++ perl/<code>

第三步 安裝libfastcommon

大家可以直接到github上面先找到ibfastcommon。這裡為了大家方便查找。我已經把把鏈接給大家整理好了libfastcommon這裡大家可以先下載到本地,然後再傳至服務器上面。這裡我使用的是FinalShell。具體操作就不在這裡詳細說了。這裡目錄為:/root/fastdfs/下載下來的是zip格式壓縮包,然後,我們解壓zip

<code>

unzip

libfastcommon-master

.zip

/<code>

進入到剛剛解壓的文件裡面

<code>

cd

libfastcommon-master//<code>

進行編譯安裝

<code>./

make

.sh && ./

make

.sh install/<code>
用asp.net core結合fastdfs打造分佈式文件存儲系統

``shell./make.sh && ./make.sh install

<code> 
切換到配置文件目錄下
```shell

cd

/etc/fdfs//<code>

查看所有配置文件使用ls命令就行了

用asp.net core結合fastdfs打造分佈式文件存儲系統

去除所有的.sample後綴

<code>

cp

client

.conf

.sample

client

.conf

cp

storage

.conf

.sample

storage

.conf

cp

storage_ids

.conf

.sample

storage_ids

.conf

cp

tracker

.conf

.sample

tracker

.conf

/<code>

我們再次使用ls命令查看

用asp.net core結合fastdfs打造分佈式文件存儲系統

可以看到我們已經去除了所有的.sample後綴下一步,創建文件夾,分別創建client,storage,tracker文件夾

<code>mkdir -p /home/software/fastfdfs/

data

/client mkdir -p /home/software/fastfdfs/

data

/storage mkdir -p /home/software/fastfdfs/

data

/tracker/<code>

client.config配置

下面我們開始配置文件修改,首先修改的是clietn.conf,使用vim編輯器進行文件的編輯

<code>

vim

client

.conf

/<code>

使用 :set number 顯示行數修改第11行,確定日誌文件的存儲路徑,這裡用我們剛剛創建client文件夾路徑

base_path = /home/software/fastdfs/data/client

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改第22 行,tracker的地址與端口,根據實際情況進行修改,我的是192.168.1.14。

這裡貌似有兩個tracker_server,畢竟是單機版部署因此我這裡註釋了一個tracker_server = 192.168.1.14:22122

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改號之後記得保存修改!

storage.conf修改

下面我們修改storage.conf。同上是用vim進行修改首先我們修改下11行的分組設置

group_name = group1

用asp.net core結合fastdfs打造分佈式文件存儲系統

接下來修改49行的基礎路徑,這裡也用上面我們已經創建的路徑

base_path = /home/software/fastdfs/data/storage

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改第129行的storage路徑。

store_path0 = /home/software/fastdfs/data/storage注意:這裡提示下:存儲路徑的順序很重要,不要弄亂了,基礎路徑應該獨立於存儲路徑,即儘量讓這兩個路徑不一樣。這裡我設置了一樣

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改第145行 ,配置tracker的IP地址與端口。至於如何填寫這個地址,上面配置client.conf的時候,我們就已經說過了。

tracker_server = 192.168.1.14:22122

用asp.net core結合fastdfs打造分佈式文件存儲系統

tracker.conf 配置

現在修改tracker.conf文件修改第23行,修改tracker的基礎路徑。這個路徑的文件夾我們剛剛在上面已經創建過了

base_path = /home/software/fastdfs/data/tracker

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改第57行 歸屬組設置

store_group = group1

用asp.net core結合fastdfs打造分佈式文件存儲系統

第六步 啟動tracker 與storage

啟動tracker

<code>

fdfs_trackerd

/etc/fdfs/tracker.conf start/<code>

啟動storage

<code>

fdfs_storaged

/etc/fdfs/storage.conf start/<code>

測試是否啟動成功,我們嘗試上傳文件。我的root文件夾下有一張圖片

<code>

fdfs_test

/etc/fdfs/client.conf upload /root/fastdfs/test.jpeg/<code>

如上圖所示表示上傳成功。

第七步 安裝nginx訪問圖片

這裡由於服務器已經安裝了nginx,因此需要在現有的nginx的版本基礎上安裝新的fastdfs的模塊。,安裝方法可能略有不同首先查看下nginx的版本

<code>

nginx

-V/<code>

這裡需要注意一下,如果你服務器上沒有安裝過nginx則需要先安裝nginx的依賴,然後再進行安裝

<code>

yum

-y install gcc-c++ zlib-devel pcre-devel/<code>

如下圖所示:

用asp.net core結合fastdfs打造分佈式文件存儲系統

因此我們需要下載對應版本的源碼

<code>wget http:/<code>

解壓安裝包

<code>

tar

zxvf

nginx-1

.14

.0

.tar

.gz

/<code>

下載nginx的fastdfs模塊,這裡我已經幫大家把這個項目github地址整理好了。astdfs-nginx-module這這裡我們可以先下載到自己電腦上,再上傳服務器。進行解壓

<code>unzip fastdfs-nginx-

module

-

master

.

zip

/<code>

這裡由於我們已經安裝了nginx,因此需要備份之前的nginx防止出現問題進行還原

<code>

mv

/usr/sbin/nginx /usr/sbin/nginx.back

cp

-rf /etc/nginx/ /etc/nginx.back

/<code>

切換到nginx壓縮包的解壓目錄裡面去

<code>

cd

nginx-1

.16

.1

/<code>

進行配置檢測並且添加模塊

<code>./configure --prefix=

/etc/

nginx/ --sbin-path=

/usr/

sbin/nginx --add-

module

=

/root/

fastdfs/fastdfs-nginx-

module

-master/src//<code>

--prefix : nginx的安裝目錄--add-module:解壓後模塊所在目錄,記得要加上src

執行如下命令進行nginx的重新編譯,如果之前已經安裝了nginx則不能執行install,否則會覆蓋之前的配置文件,也可以在安裝後再把之前的配置覆蓋過來。

<code>

make

&&

make

install/<code>

拷貝編譯後的nginx到原來的安裝目錄

<code>

cp

objs/nginx /usr/sbin/nginx/<code>

重啟nginx

<code>systemctl restart nginx
 
nginx -s reload

 
nginx/<code>

切換至fastdfs的解壓包裡面去

<code>

cd

/root/fastfdfs/fastdfs-master/<code>

進行文件的複製

<code>

cp

conf/http.conf /etc/fdfs/

cp

conf/mime.types /etc/fdfs/

/<code>

切換至fastdfs-nginx-module的解壓目錄下面

<code>cd /root/fastdfs/fastdfs-nginx-

module

-

master

/<code>

進行配置文件的複製

<code> 

cp

src/mod_fastdfs.conf /etc/fdfs//<code>

mod_fastdfs.conf配置

修改/etc/fdfs下面的mod_fastdfs.conf配置文件

<code>

vim

/etc/fdfs/mod_fastdfs.conf/<code>

修改 40行,更改tracker的ip地址與端口

tracker_server=192.168.1.14:22122

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改48行的group_name

用asp.net core結合fastdfs打造分佈式文件存儲系統

修改53行 url地址中是否包含組名,如果鏈接中包含group則需要設置為true

<code>

url_have_group_name

=

true

/<code>
用asp.net core結合fastdfs打造分佈式文件存儲系統

修改62行 storage所在目錄

<code>

store_path0

=/home/software/fastdfs/data/storage/<code>
用asp.net core結合fastdfs打造分佈式文件存儲系統

保存修改。

nginx修改

這裡注意,每個笨的配置文件存儲的方式不同,此步驟可能有所不同。進行nginx配置文件的修改,我們可以切換到nginx的安裝目錄下面。也可以直接輸入如下命令

注意:這裡我的nginx版本為1.16.1,配置文件為conf.d下面的所有的*.conf文件。因此我的輸入命令如下:

<code>vim /etc/nginx/conf.d/

default

.conf /<code>

進行nginx.conf的修改,如下圖所示:

<code>      

server

{

listen

8888;

server_name

192.168.1.93;

location

~/group([0-9])/M00 {

root

/home/software/fastdfs/data/storage/data;

ngx_fastdfs_module;

}

}

/<code>
用asp.net core結合fastdfs打造分佈式文件存儲系統

多個group的配置

當配置多個組,且mod_fastdfs.conf 裡面指定了url_have_group_name= true 時,配置方式:

<code>location ~  /group([

0

-

9

]) /M00 { root /home/software/fastdfs/

data

/storage/

data

; ngx_fastdfs_module; }/<code>

比如:在group1上的 nginx 的nginx.conf 配置是

<code>location  /group1/M00 {
     root /home/software/fastdfs/

data

/storage/

data

; ngx_fastdfs_module; }/<code>

比如:在group2上的 nginx 的nginx.conf 配置是

<code>location   /group2/M00 {
     root /home/software/fastdfs/

data

/storage/

data

; ngx_fastdfs_module; }/<code>

查看nginx的配置文件是否有效

<code>

nginx

-t/<code>
用asp.net core結合fastdfs打造分佈式文件存儲系統

顯示 is ok沒有任何問題,現在啟動nginx。這裡我配置為開機自動啟動

<code>

systemctl

restart nginx

第二種方式

nginx

-s reload

/<code>

依照上面的方式,我們重新上傳一張圖片。並進行訪問測試是否啟動成功,我們嘗試上傳文件。我的root文件夾下有一張圖片

<code>

fdfs_test

/etc/fdfs/client.conf upload /root/fastdfs/test.jpeg/<code>

上傳成功,訪問下面的地址

用asp.net core結合fastdfs打造分佈式文件存儲系統

路徑說明

group1:表示這張圖片被保存在了哪個組當中,M00:代表磁盤目錄,如果電腦只有一個磁盤那就只有M00, 如果有多個磁盤,那就M01、M02...等等。00/00:代表磁盤上的兩級目錄,每級目錄下是從00到FF共256個文件夾,兩級就是256*256個。wKgBE1r-fICAcd3kAAHY-4ojheI481.jpeg表示被存儲到storage上的test.jpeg被重命名的名字,這樣做的目的是為了防止圖片名字重複。我們到單機版的數據目錄下查看一下是否有我們剛才上傳的圖片,發現是有的返回的圖片信息中明確說明了存儲在了group1下面。

FastDFS集群版的安裝

這裡集群版的安裝是在單機版的基礎上安裝的。

機器準備

用asp.net core結合fastdfs打造分佈式文件存儲系統

安裝步驟

軟件準備

在所有服務器上創建如下目錄用來存儲需要安裝的軟件包

<code>mkdir -p ~

/fastdfs/

/<code>

在tracker1上下載所需要的軟件安裝包,並放在如下目錄下

<code>~

/fastdfs/

/<code>

查看如下所示:

用asp.net core結合fastdfs打造分佈式文件存儲系統

進入對應的文件夾並拷貝對應的文件到其他幾臺服務器上:

<code>scp ./* root@192.

168.1

.

15

:~/fastdfs/

scp ./* root@192.

168.1

.

16

:~/fastdfs/

scp ./* root@192.

168.1

.

93

:~/fastdfs/

scp ./* root@192.

168.1

.

197

:~/fastdfs/

/<code>

在storage服務器及tracker服務器都創建如下的文件夾,用來存放fastdfs的數據及日誌文件

<code>

mkdir

-p /home/software/fastfdfs/client

mkdir

-p /home/software/fastfdfs/storage

mkdir

-p /home/software/fastfdfs/tracker/<code>

在所有服務器上執行·如下命令安裝相關工具

<code> 

yum

-y install vim wget unzip/<code>

在所有sotrage及tracker服務器上執行如下命令安裝相關依賴

<code>

yum

install -y gcc-c++ perl/<code>

安裝

這些操作需要在所有storage及tracker服務器上都進行操作

storage及tracker服務器安裝libfastcommon

在需要安裝fastdfs的storage及tracker服務器上安裝libfastcommon這裡我們進入安裝文件目錄~/fastdfs/,執行如下命令,解壓並編譯安裝:

<code>unzip libfastcommon-master.zip
cd libfastcommon-master/
./

make

.sh && ./

make

.sh install/<code>

出現如下圖所示則表示安裝成功

用asp.net core結合fastdfs打造分佈式文件存儲系統

storage及tracker服務器安裝fastdfs

在需要安裝fastdfs的storage及tracker服務器上安裝fastdfs這裡我們進入安裝文件目錄~/fastdfs/,執行如下命令,解壓並編譯安裝:

<code>

unzip

fastdfs-master.zip

cd

fastdfs-master/

&& ./make.sh install

cp

conf/http.conf /etc/fdfs/

cp

conf/mime.types /etc/fdfs/

/<code>

進行文件的複製

<code>

cp

conf/http.conf /etc/fdfs/

cp

conf/mime.types /etc/fdfs/

/<code>

修改配置文件

切換到配置文件目錄,並執行如下命令去除所有的.sample後綴:

<code>

cd

/etc/fdfs/

cp

client.conf.sample client.conf

cp

storage.conf.sample storage.conf

cp

storage_ids.conf.sample storage_ids.conf

cp

tracker.conf.sample tracker.conf

/<code>

修改client.config配置

需要測試的時候才需要配置此client.conf配置

<code>

vim

client

.conf

/<code>

修改內容如下:

<code>

11

base_path

=

/home/software/fastdfs/client

22

tracker_server

=

192.168

.1

.14

:22122

/<code>

vim修改storage.conf 修改內容如下:

<code>

11

group_name

=

group1

49

base_path

=

/home/software/fastdfs/storage

129

/home/software/fastdfs/data/storage

145

tracker_server

=

192.168

.1

.14

:22122

/<code>

vim修改tracker.conf配置

這裡只有tracker服務器才需要配置此配置及 14跟16

修改內容如下:

<code>

23

base_path = /home/software/fastdfs/tracker

57

store_group = group1

/<code>

安裝nginx

只有sotrage服務器及vip服務器需要安裝nginx用來提供文件的訪問在安裝nginx前需要安裝相關的依賴並下載對應的源碼,執行如下命令。此文件也保存到文件安裝目錄

<code>

yum

-y install gcc-c++ zlib-devel pcre-devel

wget

http://nginx.org/download/nginx-1.16.1.tar.gz

tar

zxvf nginx-1.14.0.tar.gz

/<code>

解壓nginx的fastdfs模塊,並進入到文件夾下進行配置文件的複製

nginx的fastdfs模塊在所有storage服務器上進行安裝

<code>unzip fastdfs-nginx-

module

-

master

.

zip

cd ~

/fastdfs/fastdfs

-nginx-

module

-

master

cp src/mod_fastdfs.conf /etc/fdfs//<code>

進入到nginx源碼目錄,進行配置檢測並添加對應的fastdfs模塊。然後進行nginx的安裝

<code>./configure --prefix=

/etc/

nginx/ --sbin-path=

/usr/

sbin/nginx --add-

module

=/root/fastdfs/fastdfs-nginx-

module

-master/src/ make && make install #如有需要可以拷貝nginx到sbin目錄 cp objs/nginx /usr/sbin/nginx/<code>

mod_fastdfs.conf配置

vim修改/etc/fdfs下面的mod_fastdfs.conf配置文件

<code>

40

tracker_server=192.168.1.14:22122

48

group_name=group1

53

url_have_group_name

=

true

62

store_path0=/home/software/fastdfs/storage

/<code>

nginx配置修改

只有sotrage服務器及vip服務器需要安裝,group1的服務器需要新增如下server配置,root路徑為storage的data路徑

<code>      

server

{

listen

8888;

server_name

192.168.1.93;

location

/group1/M00 {

root

/home/software/fastdfs/storage/data;

ngx_fastdfs_module;

}

}

/<code>

group2的服務器上新增如下server配置,root路徑為storage的data路徑

<code>      

server

{

listen

8888;

server_name

192.168.1.93;

location

/group2/M00 {

root

/home/software/fastdfs/storage/data;

ngx_fastdfs_module;

}

}

/<code>

vip服務器的nginx配置如下:

<code> 

upstream

fdfs_group1 {

server

192.168.1.14:8888

weight=

1

max_fails=

2

fail_timeout=

30s

;

server

192.168.1.15:8888

weight=

1

max_fails=

2

fail_timeout=

30s

; }

upstream

fdfs_group2 {

server

192.168.1.16:8888

weight=

1

max_fails=

2

fail_timeout=

30s

;

server

192.168.1.93:8888

weight=

1

max_fails=

2

fail_timeout=

30s

; }

server

{

listen

80

;

server_name

192.168.1.197

;

location

/ {

root

html;

index

index.html index.htm; }

location

/group1/M00 {

proxy_set_header

Host

$host

;

proxy_set_header

X-Real-IP

$remote_addr

;

proxy_pass

http://fdfs_group1; }

location

/group2/M00 {

proxy_set_header

Host

$host

;

proxy_set_header

X-Real-IP

$remote_addr

;

proxy_pass

http://fdfs_group2; } }/<code>

查看nginx的配置文件是否有效

<code>

nginx

-t/<code>

顯示 is ok沒有任何問題,現在啟動nginx。這裡我配置為開機自動啟動

<code> 
systemctl restart nginx
 
nginx -s reload

 
nginx/<code>

依照上面的方式,我們重新上傳一張圖片。並進行訪問測試是否啟動成功,我們嘗試上傳文件。我的root文件夾下有一張圖片

<code>

fdfs_test

/etc/fdfs/client.conf upload /root/fastdfs/test.jpeg/<code>

上傳成功,訪問下面的地址

用asp.net core結合fastdfs打造分佈式文件存儲系統

asp.net core操作FastDFS

這裡的微服務是基於abp vnext的。只是在應用層進行相關的服務開發。這裡需要說明下,這裡我是使用工廠模式,根據配置來創建對應的文件操作提供者,進而對對應的文件服務器上的文件進行操作的。這裡我只摘錄FastDFS相關的代碼供大家參考。

第一步 安裝對應的nuget包

這裡我用的是更新時間相對比較近下載量相對比較多的作者的nuget包,

<code>

Install

-

Package

FastDFSNetCore -

Version

1.2

.5

/<code>

第二步,編寫一個FastDFSProvider

具體的代碼如下所示:

<code>  
     
     
     
     

public

class

FastDFSProvider

:

IStorageProvider

,

ITransientDependency

{

public

string

ProviderName =>

"FastDFS"

;

private

readonly

FastDFSConfig _fastDFSConfig;

public

FastDFSProvider

(

FastDFSConfig fastDFSConfig

)

{ _fastDFSConfig = fastDFSConfig ??

throw

new

ArgumentNullException(

nameof

(fastDFSConfig));

string

[] trackers = fastDFSConfig.Trackers.Split(

','

, StringSplitOptions.RemoveEmptyEntries);

var

trackerIPs =

new

List();

foreach

(

var

onetracker

in

trackers) { trackerIPs.Add(

new

IPEndPoint(IPAddress.Parse(onetracker), fastDFSConfig.Port)); } ConnectionManager.Initialize(trackerIPs); }

public

async

Task<

bool

>

DeleteObjectByNameAsync

(

string

objectName,

string

groupName =

""

)

{ RestoreFileName(objectName,

out

string

fileName,

out

groupName);

await

FastDFSClient.RemoveFileAsync(groupName, fileName);

return

true

; }

public

Task<

string

>

GetGateWayUrl

(

)

=> Task.FromResult(_fastDFSConfig.GateWayUrl);

public

async

Task<

byte

[]> GetObjectByteAsync(

string

objectName,

string

groupName =

""

) { RestoreFileName(objectName,

out

string

fileName,

out

groupName);

var

storageNode =

await

GetStorageNodeAsync(groupName);

return

await

FastDFSClient.DownloadFileAsync(storageNode, fileName); }

public

async

Task

GetObjectStreamAsync

(

string

objectName,

string

groupName =

""

)

{ RestoreFileName(objectName,

out

string

fileName,

out

groupName);

var

storageNode =

await

GetStorageNodeAsync(groupName); FDFSFileInfo fileInfo =

await

FastDFSClient.GetFileInfoAsync(storageNode, fileName); Stream stream =

new

MemoryStream();

int

limit =

1024

*

100

;

if

(fileInfo.FileSize >= limit) {

long

offset =

0

;

long

len = limit;

while

(len >

0

) {

byte

[] buffer =

await

FastDFSClient.DownloadFileAsync(storageNode, fileName, offset, len); stream.Write(buffer,

0

,

int

.Parse(len.ToString())); stream.Flush(); offset += len; len = (fileInfo.FileSize - offset) >= limit ? limit : (fileInfo.FileSize - offset); } }

else

{

byte

[] buffer =

await

FastDFSClient.DownloadFileAsync(storageNode, fileName); stream.Write(buffer,

0

, buffer.Length); stream.Flush(); } stream.Seek(

0

, SeekOrigin.Begin);

return

stream; }

public

async

Task<

string

>

StoreObjectByteAsync

(

byte

[] objectByte,

string

objectName,

string

groupName =

""

)

{

var

storageNode =

await

GetStorageNodeAsync(

""

);

var

filePath =

await

FastDFSClient.UploadFileAsync(storageNode, objectByte, Path.GetExtension(objectName));

return

storageNode.GroupName +

"/"

+ filePath; }

public

async

Task<

string

>

StoreObjectStreamAsync

(

Stream objectStream,

string

objectName,

string

groupName =

""

)

{

var

storageNode =

await

GetStorageNodeAsync(

""

);

var

filePath =

await

FastDFSClient.UploadFileAsync(storageNode, objectStream, Path.GetExtension(objectName));

return

storageNode.GroupName +

"/"

+ filePath; } }/<code>

第三步 在分佈式配置中心中進行配置

這裡的分佈式配置中心中進行FastDFS的配置如下:

<code>

"ObjectStorage"

: {

"StoreType"

:

"FastDFS"

,

"FastDFS"

: {

"Trackers"

:

"192.168.1.14,192.168.1.16"

,

"Port"

: 22122,

"GateWayUrl"

:

"http://192.168.1.197"

} }/<code>

第四步 文件提供者工廠方法中加入FastDFS的處理

工廠方法根據上面配置中的StoreType從而知道對應的存儲是往FastDFS集群進行存儲的,進而再加載對應的FastDFS的配置,然後創建上面的FastDFSProvider。主要代碼摘錄如下:

<code>  

case

"RondsDFS"

: {

var

rondsDFSSection = _configuration.GetSection(

"ObjectStorage:RondsDFS"

);

if

(rondsDFSSection ==

null

)

throw

new

ArgumentNullException(

"RondsDFS is not configured![OObjectStorage:RondsDFS]"

); StorageProvider = _serviceProvider.GetRequiredService();

break

; }/<code>

總結

今天首先給大家講解了如何搭建FastDFS單機版及集群版,然後講解了如何通過asp.net core對FastDFS進行操作。當然由於我的文件系統對應多個文件存儲程序,因此這裡只是把FastDFS作為其中一個提供者進行處理。大夥可以只參考其中的操作方法即可。本來想錄個視頻的,畢竟安裝過程稍顯複雜,奈何機器不允許,Pass了!


分享到:


相關文章: