中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站上傳后優(yōu)化大師apk

網(wǎng)站上傳后,優(yōu)化大師apk,wordpress永久鏈接設(shè)置的六大技巧,哪個網(wǎng)站做效果圖好引言 文件存儲已成為一個做任何應(yīng)用都不可回避的需求。傳統(tǒng)的單機文件存儲方案在面對大規(guī)模數(shù)據(jù)和高并發(fā)訪問時往往力不從心,而分布式文件存儲系統(tǒng)則提供了更好的解決方案。本篇文章我將基于Spring Boot 3 為大家講解如何基于MinIO來實現(xiàn)分布式文件存儲。 分布式存…

引言

文件存儲已成為一個做任何應(yīng)用都不可回避的需求。傳統(tǒng)的單機文件存儲方案在面對大規(guī)模數(shù)據(jù)和高并發(fā)訪問時往往力不從心,而分布式文件存儲系統(tǒng)則提供了更好的解決方案。本篇文章我將基于Spring Boot 3 為大家講解如何基于MinIO來實現(xiàn)分布式文件存儲。

分布式存儲的出現(xiàn)

在探討核心內(nèi)容之前,我們不妨先回顧分布式存儲技術(shù)是如何伴隨系統(tǒng)架構(gòu)演變發(fā)展的。在單體架構(gòu)早期,文件直接存儲于應(yīng)用服務(wù)器中,這種方式簡單直接,存取便捷。然而,隨著業(yè)務(wù)規(guī)模擴大和用戶量激增,系統(tǒng)架構(gòu)逐步向分布式或微服務(wù)方向演進(jìn)。此時,若仍將文件存儲在應(yīng)用服務(wù)器中,在負(fù)載均衡機制下可能導(dǎo)致文件訪問異常 —— 用戶上傳的文件可能因路由到其他服務(wù)節(jié)點而無法訪問。
在這里插入圖片描述面對這個挑戰(zhàn),我們可以借鑒"分層解決"的架構(gòu)思想:將文件存儲從應(yīng)用服務(wù)中剝離,集中在獨立的存儲服務(wù)中統(tǒng)一管理。這便是分布式文件存儲系統(tǒng)的雛形。

技術(shù)選型

在了解了分布式存儲的演進(jìn)背景后,讓我們來梳理當(dāng)前主流的分布式存儲解決方案。

其他

  • FastDFS -> 架構(gòu)老舊,社區(qū)活躍度低,文檔資料匱乏
  • Ambry -> 過度依賴 LinkedIn 技術(shù)棧,通用性不足
  • MooseFS -> 部署配置繁瑣,運維門檻高
  • MogileFS -> 性能一般,擴展性受限
  • LeoFS -> 更新維護緩慢,生態(tài)系統(tǒng)不完善
  • openstack -> 架構(gòu)復(fù)雜重量級,不適合輕量級應(yīng)用
  • TFS -> 主要服務(wù)于阿里內(nèi)部,外部支持有限
  • ceph -> 學(xué)習(xí)曲線陡峭,配置調(diào)優(yōu)復(fù)雜
  • GlusterFS -> 架構(gòu)復(fù)雜,問題定位困難
  • OSS -> 商業(yè)收費服務(wù),成本隨數(shù)據(jù)量增長

?MinIO

MinIO 是一款輕量級的分布式對象存儲系統(tǒng),完全兼容 Amazon S3 云存儲服務(wù)接口。其部署維護簡單,性能卓越,成為我們的首選方案。

MinIO安裝

MinIO 提供了多種部署方式,包括單機部署和分布式部署。本文主要關(guān)注 Spring BootMinIO 的整合實踐,因此我們選擇使用Docker(Ps:沒安裝Docker的同學(xué)速速去安裝,或者用別的方式只要本地部署的能跑就行)進(jìn)行快速部署。

首先,通過命令拉取鏡像。

docker pull minio/minio

接著在 本地創(chuàng)建一個存儲文件的映射目錄 D:\minio\data(Ps:我當(dāng)前演示的環(huán)境是win系統(tǒng),大家根據(jù)自己的操作系統(tǒng)建個目錄就行),使用以下命令啟動 MinIO:

🔑 補充一個小細(xì)節(jié):MinIO 的安全限制要求用戶名長度至少需要 3 個字符,密碼長度至少需要 8 個字符。

    docker run -d --name minio -p 9000:9000 -p 9001:9001 -v D:\minio\data:/data -e "MINIO_ROOT_USER=root" -e "MINIO_ROOT_PASSWORD=12345678" minio/minio server /data --console-address ":9001" --address ":9000"

參數(shù)說明:

  • -d: 后臺運行容器
  • --name: 容器名稱
  • -p: 端口映射,9000用于API訪問,9001用于控制臺訪問
  • -v: 目錄映射,將本地目錄映射到容器的 /data
  • -e: 環(huán)境變量,設(shè)置管理員賬號和密碼
  • --console-address: 指定控制臺端口
  • --restart=always: 容器自動重啟策略
  • --address ":9000": 顯式指定 API 端口

運行成功后訪問 http://localhost:9001,使用執(zhí)行命令中的憑據(jù)(Ps:大家在使用時可以修改為自己的用戶名和密碼)登錄:

  • 用戶名:root
  • 密碼:12345678
    在這里插入圖片描述登錄系統(tǒng)后,界面會提示創(chuàng)建。熟悉云服務(wù)商OSS服務(wù)的讀者對此概念應(yīng)該不陌生。對初次接觸的讀者,可以將理解為一個命名空間或文件夾,您可以創(chuàng)建多個,每個內(nèi)還能包含多層級的文件夾和文件。

在這里插入圖片描述這里我演示下控制臺如何建桶和上傳文件,方便大家理解文件在MinIO上的存儲結(jié)構(gòu)。
在這里插入圖片描述只需要輸入名稱就可以,建好之后可以看到的使用狀態(tài)。
在這里插入圖片描述點擊它進(jìn)入的內(nèi)部,這里大家需要關(guān)注一個設(shè)置- Access Policy,默認(rèn)是Private。這個設(shè)置需要根據(jù)業(yè)務(wù)的實際情況來,如果你的業(yè)務(wù)是需要提供一些不需要鑒權(quán)的公共訪問的文件,就設(shè)為public;反之,就保持private。我這里把它修改為public
在這里插入圖片描述然后點擊右上角的上傳按鈕進(jìn)入上傳頁可以向桶內(nèi)上傳文件。
在這里插入圖片描述上傳成功后可以在桶內(nèi)看到文件。
在這里插入圖片描述點擊文件可查看詳情,支持預(yù)覽、刪除、分享等多種功能。這些操作較為直觀,安裝后各位讀者可自行體驗。本文重點關(guān)注不在控制臺的操作,就不做過多贅述了。

🔑這里再強調(diào)一點:存儲在里的文件通過API訪問的端口和控制臺是不一樣的。如果你對這里感覺迷惑,可以回看一下上面我貼上的docker運行命令里配置了兩個端口-90009001。如果要通過API訪問查看這個文件的話,通過拼接地址/端口號/桶名/文件路徑查看,那么剛測試上傳的文件的訪問API就是http://localhost:9000/test/1.gif,在瀏覽器地址欄輸入后可以看到。
在這里插入圖片描述# Spring Boot整合MinIO

這部分對于新建項目就不贅述了,直接說下我使用的 Spring boot 版本為3.2.3,供大家參考。

1.引入依賴

pom.xml引入minIO的依賴,版本大家自己使用你當(dāng)前最新的版本即可。

<!-- minio -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>${latest.version}</version>
</dependency>

2.添加配置

在yml配置文件中配置連接信息。

# minIO配置
minio:endpoint: http://127.0.0.1:9000     # MinIO服務(wù)地址fileHost: http://127.0.0.1:9000     # 文件地址hostbucketName: wechat                  # 存儲桶bucket名稱accessKey: root                     # 用戶名secretKey: 12345678                 # 密碼

3.編寫配置類

import com.pitayafruits.utils.MinIOUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@Data
public class MinIOConfig {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.fileHost}")private String fileHost;@Value("${minio.bucketName}")private String bucketName;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Beanpublic MinIOUtils creatMinioClient() {return new MinIOUtils(endpoint, fileHost, bucketName, accessKey, secretKey);}
}

4.引入工具類

這個工具類封裝了MinIO的核心功能,為您提供了很多開箱即用的功能。通過引入它,可以輕松實現(xiàn)文件上傳、下載等操作,讓大家將更多精力集中在業(yè)務(wù)開發(fā)上。

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;/*** MinIO工具類*/
@Slf4j
public class MinIOUtils {private static MinioClient minioClient;private static String endpoint;private static String fileHost;private static String bucketName;private static String accessKey;private static String secretKey;private static final String SEPARATOR = "/";public MinIOUtils() {}public MinIOUtils(String endpoint, String fileHost, String bucketName, String accessKey, String secretKey) {MinIOUtils.endpoint = endpoint;MinIOUtils.fileHost = fileHost;MinIOUtils.bucketName = bucketName;MinIOUtils.accessKey = accessKey;MinIOUtils.secretKey = secretKey;createMinioClient();}/*** 創(chuàng)建基于Java端的MinioClient*/public void createMinioClient() {try {if (null == minioClient) {log.info("開始創(chuàng)建 MinioClient...");minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();createBucket(bucketName);log.info("創(chuàng)建完畢 MinioClient...");}} catch (Exception e) {log.error("MinIO服務(wù)器異常:{}", e);}}/*** 獲取上傳文件前綴路徑* @return*/public static String getBasisUrl() {return endpoint + SEPARATOR + bucketName + SEPARATOR;}/******************************  Operate Bucket Start  ******************************//*** 啟動SpringBoot容器的時候初始化Bucket* 如果沒有Bucket則創(chuàng)建* @throws Exception*/private static void createBucket(String bucketName) throws Exception {if (!bucketExists(bucketName)) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/***  判斷Bucket是否存在,true:存在,false:不存在* @return* @throws Exception*/public static boolean bucketExists(String bucketName) throws Exception {return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/*** 獲得Bucket的策略* @param bucketName* @return* @throws Exception*/public static String getBucketPolicy(String bucketName) throws Exception {String bucketPolicy = minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());return bucketPolicy;}/*** 獲得所有Bucket列表* @return* @throws Exception*/public static List<Bucket> getAllBuckets() throws Exception {return minioClient.listBuckets();}/*** 根據(jù)bucketName獲取其相關(guān)信息* @param bucketName* @return* @throws Exception*/public static Optional<Bucket> getBucket(String bucketName) throws Exception {return getAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();}/*** 根據(jù)bucketName刪除Bucket,true:刪除成功; false:刪除失敗,文件或已不存在* @param bucketName* @throws Exception*/public static void removeBucket(String bucketName) throws Exception {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/******************************  Operate Bucket End  ******************************//******************************  Operate Files Start  ******************************//*** 判斷文件是否存在* @param bucketName 存儲桶* @param objectName 文件名* @return*/public static boolean isObjectExist(String bucketName, String objectName) {boolean exist = true;try {minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {exist = false;}return exist;}/*** 判斷文件夾是否存在* @param bucketName 存儲桶* @param objectName 文件夾名稱* @return*/public static boolean isFolderExist(String bucketName, String objectName) {boolean exist = false;try {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());for (Result<Item> result : results) {Item item = result.get();if (item.isDir() && objectName.equals(item.objectName())) {exist = true;}}} catch (Exception e) {exist = false;}return exist;}/*** 根據(jù)文件前置查詢文件* @param bucketName 存儲桶* @param prefix 前綴* @param recursive 是否使用遞歸查詢* @return MinioItem 列表* @throws Exception*/public static List<Item> getAllObjectsByPrefix(String bucketName,String prefix,boolean recursive) throws Exception {List<Item> list = new ArrayList<>();Iterable<Result<Item>> objectsIterator = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());if (objectsIterator != null) {for (Result<Item> o : objectsIterator) {Item item = o.get();list.add(item);}}return list;}/*** 獲取文件流* @param bucketName 存儲桶* @param objectName 文件名* @return 二進(jìn)制流*/public static InputStream getObject(String bucketName, String objectName) throws Exception {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 斷點下載* @param bucketName 存儲桶* @param objectName 文件名稱* @param offset 起始字節(jié)的位置* @param length 要讀取的長度* @return 二進(jìn)制流*/public InputStream getObject(String bucketName, String objectName, long offset, long length)throws Exception {return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());}/*** 獲取路徑下文件列表* @param bucketName 存儲桶* @param prefix 文件名稱* @param recursive 是否遞歸查找,false:模擬文件夾結(jié)構(gòu)查找* @return 二進(jìn)制流*/public static Iterable<Result<Item>> listObjects(String bucketName, String prefix,boolean recursive) {return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());}/*** 使用MultipartFile進(jìn)行文件上傳* @param bucketName 存儲桶* @param file 文件名* @param objectName 對象名* @param contentType 類型* @return* @throws Exception*/public static ObjectWriteResponse uploadFile(String bucketName, MultipartFile file,String objectName, String contentType) throws Exception {InputStream inputStream = file.getInputStream();return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream, inputStream.available(), -1).build());}/*** 上傳本地文件* @param bucketName 存儲桶* @param objectName 對象名稱* @param fileName 本地文件路徑*/public static String uploadFile(String bucketName, String objectName,String fileName, boolean needUrl) throws Exception {minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());if (needUrl) {String imageUrl = fileHost+ "/"+ bucketName+ "/"+ objectName;return imageUrl;}return "";}/*** 通過流上傳文件** @param bucketName 存儲桶* @param objectName 文件對象* @param inputStream 文件流*/public static ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) throws Exception {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(), -1).build());}public static String uploadFile(String bucketName, String objectName, InputStream inputStream, boolean needUrl) throws Exception {minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(), -1).build());if (needUrl) {String imageUrl = fileHost+ "/"+ bucketName+ "/"+ objectName;return imageUrl;}return "";}/*** 創(chuàng)建文件夾或目錄* @param bucketName 存儲桶* @param objectName 目錄路徑*/public static ObjectWriteResponse createDir(String bucketName, String objectName) throws Exception {return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());}/*** 獲取文件信息, 如果拋出異常則說明文件不存在** @param bucketName 存儲桶* @param objectName 文件名稱*/public static String getFileStatusInfo(String bucketName, String objectName) throws Exception {return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()).toString();}/*** 拷貝文件** @param bucketName 存儲桶* @param objectName 文件名* @param srcBucketName 目標(biāo)存儲桶* @param srcObjectName 目標(biāo)文件名*/public static ObjectWriteResponse copyFile(String bucketName, String objectName,String srcBucketName, String srcObjectName) throws Exception {return minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder().bucket(bucketName).object(objectName).build()).bucket(srcBucketName).object(srcObjectName).build());}/*** 刪除文件* @param bucketName 存儲桶* @param objectName 文件名稱*/public static void removeFile(String bucketName, String objectName) throws Exception {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());}/*** 批量刪除文件* @param bucketName 存儲桶* @param keys 需要刪除的文件列表* @return*/public static void removeFiles(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();keys.forEach(s -> {objects.add(new DeleteObject(s));try {removeFile(bucketName, s);} catch (Exception e) {log.error("批量刪除失敗!error:{}",e);}});}/*** 獲取文件外鏈* @param bucketName 存儲桶* @param objectName 文件名* @param expires 過期時間 <=7 秒 (外鏈有效時間(單位:秒))* @return url* @throws Exception*/public static String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception {GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();return minioClient.getPresignedObjectUrl(args);}/*** 獲得文件外鏈* @param bucketName* @param objectName* @return url* @throws Exception*/public static String getPresignedObjectUrl(String bucketName, String objectName) throws Exception {GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).method(Method.GET).build();return minioClient.getPresignedObjectUrl(args);}/*** 將URLDecoder編碼轉(zhuǎn)成UTF8* @param str* @return* @throws UnsupportedEncodingException*/public static String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");return URLDecoder.decode(url, "UTF-8");}/******************************  Operate Files End  ******************************/}

5.開發(fā)測試

我剛好在做練手項目,這里寫個上傳頭像的接口。

import com.pitayafruits.base.BaseInfoProperties;
import com.pitayafruits.config.MinIOConfig;
import com.pitayafruits.grace.result.GraceJSONResult;
import com.pitayafruits.grace.result.ResponseStatusEnum;
import com.pitayafruits.utils.MinIOUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;@RestController
@RequestMapping("file")
public class FileController extends BaseInfoProperties {@Resourceprivate MinIOConfig minIOConfig;@PostMapping("uploadFace")public GraceJSONResult upload(@RequestParam MultipartFile file,String userId) throws Exception {if (StringUtils.isBlank(userId)) {return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_FAILD);}String filename = file.getOriginalFilename();if (StringUtils.isBlank(filename)) {return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_FAILD);}filename = "face" +  "/" + userId + "/" + filename;MinIOUtils.uploadFile(minIOConfig.getBucketName(), filename, file.getInputStream());String faceUrl = minIOConfig.getFileHost()+ "/"+ minIOConfig.getBucketName()+ "/"+ filename;return GraceJSONResult.ok(faceUrl);}}

可以看到通過工具類只需要一行代碼就可以實現(xiàn)上傳文件,我們只需要在調(diào)用的時候做好文件的業(yè)務(wù)隔離即可。完成了接口的開發(fā),這里我來通過Apifox調(diào)用測試一下。
在這里插入圖片描述通過瀏覽器訪問返回的圖片鏈接會自動下載,我們再登錄控制臺看對應(yīng)的桶下的這個路徑,也可以看到這個文件。
在這里插入圖片描述

小結(jié)

我們在集成第三方服務(wù)時應(yīng)遵循一個核心原則:將API操作封裝成通用工具類。這不僅讓MinIO的集成更加優(yōu)雅,也讓代碼具備更好的復(fù)用性和可維護性。這種思維方式同樣適用于其他第三方服務(wù)的對接。

http://www.risenshineclean.com/news/58068.html

相關(guān)文章:

  • 溫州網(wǎng)站建設(shè)風(fēng)格網(wǎng)絡(luò)熱詞英語
  • 鄉(xiāng)鎮(zhèn)政府網(wǎng)站建設(shè)自查報告培訓(xùn)心得體會范文大全1000字
  • 移動網(wǎng)站是什么意思百度廣告聯(lián)盟一個月能賺多少
  • 網(wǎng)站建設(shè)費用包括哪些內(nèi)容優(yōu)化落實新十條措施
  • 網(wǎng)站推廣策劃的思路廈門網(wǎng)
  • 寶雞市市政工程建設(shè)管理處網(wǎng)站網(wǎng)站域名查詢ip地址
  • 免費搭建網(wǎng)站 優(yōu)幫云營銷網(wǎng)站建設(shè)軟件下載
  • 青島網(wǎng)站設(shè)計哪家便宜抖音廣告投放代理商
  • 重慶網(wǎng)上找工作哪個網(wǎng)站好十大騙子教育培訓(xùn)機構(gòu)
  • 天將建設(shè)集團有限公司網(wǎng)站企業(yè)網(wǎng)絡(luò)推廣最簡單方法
  • 做網(wǎng)站建設(shè)最好的公司是seo云優(yōu)化軟件
  • 如何做公司自己的網(wǎng)站首頁seo網(wǎng)站seo
  • 大連b2c網(wǎng)站建設(shè)如何建立網(wǎng)站服務(wù)器
  • 小程序網(wǎng)站開發(fā)運行合同網(wǎng)站建設(shè)技術(shù)外包
  • 國建設(shè)委員會網(wǎng)站上查詢搜索引擎優(yōu)化的完整過程
  • 福州電子商務(wù)網(wǎng)站在線識別圖片
  • 德州匯澤網(wǎng)站建設(shè)seo發(fā)貼軟件
  • 公司設(shè)計網(wǎng)站搜索引擎營銷的名詞解釋
  • 禮品網(wǎng)站制作免費推廣
  • 網(wǎng)站開發(fā) xmind營銷網(wǎng)站建設(shè)方案
  • 請問網(wǎng)上有沒有比較好的網(wǎng)站可以做照片書的呀?要求質(zhì)量比較好的!品牌推廣方案ppt
  • 商城網(wǎng)站開發(fā)報價深圳網(wǎng)絡(luò)推廣培訓(xùn)機構(gòu)
  • 申請免費建站海外seo培訓(xùn)
  • 信譽好的揚中網(wǎng)站建設(shè)app推廣軟件有哪些
  • 四川建設(shè)廳官方網(wǎng)站文件下載企業(yè)網(wǎng)絡(luò)營銷策略
  • p2p網(wǎng)站建設(shè) 上海網(wǎng)店代運營騙局
  • 做校園網(wǎng)站 怎么備案百度推廣在哪里能看到
  • 網(wǎng)站商城定制網(wǎng)站建設(shè)蘇州seo營銷
  • 昆明網(wǎng)站開發(fā)多少錢免費域名注冊平臺
  • 做鞋子有什么好網(wǎng)站好北京seo關(guān)鍵詞排名