0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Spring多線(xiàn)程異步上傳圖片、處理水印、縮略圖

jf_ro2CN3Fa ? 來(lái)源:芋道源碼 ? 作者:芋道源碼 ? 2022-12-12 16:02 ? 次閱讀


使用環(huán)境

  • SpringBoot+FastDfs+thumbnailator
  • fdfs環(huán)境自己搞吧

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶(hù)小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶(hù)、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

thumbnailator

maven依賴(lài):

<dependency>
<groupId>net.coobirdgroupId>
<artifactId>thumbnailatorartifactId>
<version>0.4.8version>
dependency>

工具類(lèi):

importnet.coobird.thumbnailator.Thumbnails;
importnet.coobird.thumbnailator.geometry.Positions;
importorg.springframework.stereotype.Component;
importjavax.imageio.ImageIO;
importjava.io.File;
importjava.io.IOException;

@Component
publicclassPictureUtil{

/**
*水印圖片
*/
privatestaticFilemarkIco=null;

//開(kāi)機(jī)靜態(tài)加載水印圖片
static{
try{
markIco=newFile(newFile("").getCanonicalPath()+"/icon.png");
LogUtil.info(PictureUtil.class,"水印圖片加載"+(markIco.exists()?"成功":"失敗"));
}catch(Exceptione){
}
}

/**
*加水印
*/
publicvoidphotoMark(FilesourceFile,FiletoFile)throwsIOException{
Thumbnails.of(sourceFile)
.size(600,450)//尺寸
.watermark(Positions.BOTTOM_CENTER/*水印位置:中央靠下*/,
ImageIO.read(markIco),0.7f/*質(zhì)量,越大質(zhì)量越高(1)*/)
//.outputQuality(0.8f)
.toFile(toFile);//保存為哪個(gè)文件
}

/**
*生成圖片縮略圖
*/
publicvoidphotoSmaller(FilesourceFile,FiletoFile)throwsIOException{
Thumbnails.of(sourceFile)
.size(200,150)//尺寸
//.watermark(Positions.CENTER,ImageIO.read(markIco),0.1f)
.outputQuality(0.4f)//縮略圖質(zhì)量
.toFile(toFile);
}

/**
*生成視頻縮略圖(這塊還沒(méi)用到呢)
*/
publicvoidphotoSmallerForVedio(FilesourceFile,FiletoFile)throwsIOException{
Thumbnails.of(sourceFile)
.size(440,340)
.watermark(Positions.BOTTOM_CENTER,ImageIO.read(markIco),0.1f)
.outputQuality(0.8f)
.toFile(toFile);
}
}

這個(gè)插件很好用,只需集成調(diào)用即可,我記得我還試過(guò)另外幾個(gè),需要另外在linux下配置.so文件的依賴(lài)等等,查了半天也沒(méi)弄明白,很麻煩,這個(gè)方便。

這個(gè)插件又很不好用,必須要先調(diào)整尺寸,才能加水印,而且調(diào)整尺寸簡(jiǎn)直是負(fù)壓縮。壓了分辨率圖片還能變大那種。但是簡(jiǎn)單嘛,這塊不是重點(diǎn)。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶(hù)小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶(hù)、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

線(xiàn)程池

使用springboot線(xiàn)程池,方便易用,只需配置和加注解即可。

importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.core.task.TaskExecutor;
importorg.springframework.scheduling.annotation.EnableAsync;
importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
importjava.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
publicclassPoolConfig{
@Bean//returnnewAsyncResult<>(res);
publicTaskExecutortaskExecutor(){
ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();
executor.initialize();//設(shè)置核心線(xiàn)程數(shù)
executor.setCorePoolSize(4);//設(shè)置最大線(xiàn)程數(shù)
executor.setMaxPoolSize(32);//設(shè)置隊(duì)列容量
executor.setQueueCapacity(512);//設(shè)置線(xiàn)程活躍時(shí)間(秒)
executor.setKeepAliveSeconds(60);//設(shè)置默認(rèn)線(xiàn)程名稱(chēng)
executor.setThreadNamePrefix("ThreadPool-");//設(shè)置拒絕策略
executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());//等待所有任務(wù)結(jié)束后再關(guān)閉線(xiàn)程池
executor.setWaitForTasksToCompleteOnShutdown(true);
returnexecutor;
}
}

避坑知識(shí)點(diǎn):配置springboot線(xiàn)程池,類(lèi)上需要@Configuration、@EnableAsync這兩個(gè)注解,實(shí)際調(diào)用時(shí),需要遵守一個(gè)規(guī)則,即在調(diào)用的方法的類(lèi)上必須使用注解@EnableAsync,調(diào)用一個(gè)帶有@Async的方法。

比如A類(lèi)使用了注解@EnableAsync 在A類(lèi)中調(diào)用B類(lèi)的有@Async的方法,只有這樣多線(xiàn)程才生效,A類(lèi)內(nèi)調(diào)用A類(lèi)的@Async方法不生效??梢岳斫鉃镃ontroller層使用@EnableAsync注解,Service層方法上標(biāo)注@Async。這樣在Controller層調(diào)用的Service方法會(huì)從線(xiàn)程池調(diào)用線(xiàn)程來(lái)執(zhí)行。

異步邏輯:為什么要用多線(xiàn)程?

abafc9dc-794f-11ed-8abf-dac502259ad0.png

我畫(huà)了一張簡(jiǎn)單的示意圖,在這個(gè)項(xiàng)目中,客戶(hù)端一次上傳10多張圖片,每個(gè)圖片單獨(dú)上傳,等待所有圖片上傳返回200后,繼續(xù)執(zhí)行操作,如果一步一步處理,客戶(hù)端需等待服務(wù)器處理完所有邏輯,這樣浪費(fèi)沒(méi)必要的時(shí)間。顧使用異步操作,客戶(hù)端只需上傳圖片,無(wú)需等待服務(wù)器處理(我們服務(wù)器很辣雞,一個(gè)10M的圖可能要搞10多秒,見(jiàn)笑)

業(yè)務(wù)代碼

@ApiOperation("上傳業(yè)務(wù)圖片")
@PostMapping("/push/photo/{id}/{name}")
publicRpushHousingPhotoMethod(
@ApiParam("SourceId")@PathVariableIntegerid,
@ApiParam("圖片名稱(chēng)不約束,可不填則使用原名,可使用隨機(jī)碼或原名稱(chēng),但必須帶擴(kuò)展名")@PathVariable(required=false)Stringname,
@RequestParamMultipartFilefile)throwsInterruptedException,ExecutionException,IOException{
StringfileName=file.getOriginalFilename();
Stringext=StringUtils.substring(fileName,fileName.lastIndexOf('.'),fileName.length());
FiletempPhoto=File.createTempFile(UUIDUtil.make32BitUUID(),ext);
file.transferTo(tempPhoto);//轉(zhuǎn)儲(chǔ)臨時(shí)文件
service.pushPhoto(id,name,tempPhoto);
returnnewR();
}

業(yè)務(wù)代碼里隱藏了一些項(xiàng)目相關(guān)的信息,就是某些名改了,嗯。

可以看到,使用StringUtils.substring(fileName, fileName.lastIndexOf(’.’),fileName.length());這句代碼,調(diào)用apache.common.lang3工具類(lèi)獲取出了擴(kuò)展名,因?yàn)閿U(kuò)展名對(duì)圖片處理工具類(lèi)有用,他通過(guò)擴(kuò)展名識(shí)別圖片格式,所以這個(gè)必須有,如代碼,生成了一個(gè)使用隨機(jī)碼命名,但帶有.png擴(kuò)展名的臨時(shí)文件,保存在默認(rèn)臨時(shí)路徑以供處理。File.createTempFile(UUIDUtil.make32BitUUID(), ext);是生成臨時(shí)文件的方法,UUIDUtil也很簡(jiǎn)單,我貼出來(lái)吧,省著還要找

注意:controller類(lèi)上需要標(biāo)注注解@EnableAsync

/**
*生成一個(gè)32位無(wú)橫杠的UUID
*/
publicsynchronizedstaticStringmake32BitUUID(){
returnUUID.randomUUID().toString().replace("-","");
}

避坑知識(shí)點(diǎn):Spring使用MultipartFile接收文件,但不能直接把MultipartFile傳下去處理,而是保存為臨時(shí)文件,并不是多此一舉。因?yàn)?code style="font-size:14px;padding:2px 4px;margin-right:2px;margin-left:2px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">MultipartFile也是臨時(shí)文件,他的銷(xiāo)毀時(shí)間是你這個(gè)Controller層方法return的時(shí)候。

如果不使用異步,是可以在調(diào)用的方法里去處理MultipartFile文件的,但如果使用異步處理,肯定是這邊線(xiàn)程還沒(méi)處理完,那邊Controller層已經(jīng)return了,這個(gè)MultipartFile就被刪除了,于是你的異步線(xiàn)程就找不到這張圖了。那還處理個(gè)啥,對(duì)吧。所以需要手動(dòng)保存為自己創(chuàng)建的臨時(shí)文件,再在線(xiàn)程中處理完把他刪掉。

貼Service層Impl實(shí)現(xiàn)類(lèi)代碼

@Async
publicvoidpushHousingPhoto(Integerid,Stringname,Filefile)throwsInterruptedException,ExecutionException,IOException{
//存儲(chǔ)FDFS表id
LongstartTime=System.currentTimeMillis();
Integer[]numb=fastDfsService.upLoadPhoto(StringUtils.isBlank(name)?file.getName():name,file).get();
SourcePhotosContextcontext=newSourcePhotosContext();
context.setSourceId(id);
context.setNumber(numb[0]);
context.setNumber2(numb[1]);
//保存圖片關(guān)系
sourcePhotosContextService.insertNew(context);
LongendTime=System.currentTimeMillis();
LogUtil.info(this.getClass(),"source["+id+"]綁定圖片["+name+"]成功,內(nèi)部處理耗時(shí)["+(endTime-startTime)+"ms]");
//returnnewR();
}

這里的number和number2分別是帶水印的原圖和縮略圖,context是個(gè)表,用來(lái)存圖片和縮略圖對(duì)應(yīng)fdfs路徑的,就不貼了。可見(jiàn)這個(gè)方法上帶有注解@Async 所以整個(gè)方法會(huì)異步執(zhí)行。

加水印處理寫(xiě)到fdfs的service里了,這樣不算規(guī)范,可以不要學(xué)我:

@Override
publicFutureupLoadPhoto(StringfileName,MultipartFilefile)throwsIOException{
Stringext=StringUtils.substring(fileName,fileName.lastIndexOf('.'));
//創(chuàng)建臨時(shí)文件
FilesourcePhoto=File.createTempFile(UUIDUtil.make32BitUUID(),ext);
file.transferTo(sourcePhoto);
returnupLoadPhoto(fileName,sourcePhoto);
}

@Override
publicFutureupLoadPhoto(StringfileName,FilesourcePhoto)throwsIOException{
Stringext=StringUtils.substring(fileName,fileName.lastIndexOf('.'));
//創(chuàng)建臨時(shí)文件
FilemarkedPhoto=File.createTempFile(UUIDUtil.make32BitUUID(),ext);
FilesmallerPhoto=File.createTempFile(UUIDUtil.make32BitUUID(),ext);
//加水印縮圖
pictureUtil.photoMark(sourcePhoto,markedPhoto);
pictureUtil.photoSmaller(markedPhoto,smallerPhoto);
//上傳
IntegermarkedPhotoNumber=upLoadPhotoCtrl(fileName,markedPhoto);
IntegersmallerPhotoNumber=upLoadPhotoCtrl("mini_"+fileName,smallerPhoto);
//刪除臨時(shí)文件
sourcePhoto.delete();
markedPhoto.delete();
smallerPhoto.delete();
Integer[]res=newInteger[]{markedPhotoNumber,smallerPhotoNumber};
returnnewAsyncResult(res);
}

使用了方法重載,一個(gè)調(diào)用了另一個(gè),方便以后處理MultipartFile和File格式的圖片都能使用,可以見(jiàn)到使用了Future這個(gè)東西作為返回值,完全可以不這么做,正常返回就行。我懶得改了,這也是不斷探索多線(xiàn)程處理圖片的過(guò)程中,遺留下來(lái)的東西。

在service中fastDfsService.upLoadPhoto(StringUtils.isBlank(name) ? file.getName() : name, file).get()這句就是得到了這個(gè)future的內(nèi)容,可以去掉.get()Future<>??梢?jiàn)這一個(gè)小小的異步功能,其實(shí)走過(guò)了很多彎路。future其實(shí)是異步調(diào)用方法時(shí),從.get()等待異步處理的結(jié)果,等待得到結(jié)果后獲取內(nèi)容并執(zhí)行?,F(xiàn)在使用spring線(xiàn)程池處理,已經(jīng)不需要這樣做了。

以上,希望你在實(shí)現(xiàn)這個(gè)功能時(shí)可以少走彎路。

附總體示意圖:

abd5ea90-794f-11ed-8abf-dac502259ad0.png

審核編輯 :李倩

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 多線(xiàn)程
    +關(guān)注

    關(guān)注

    0

    文章

    277

    瀏覽量

    19903
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    338

    瀏覽量

    14298

原文標(biāo)題:Spring 多線(xiàn)程異步上傳圖片、處理水印、縮略圖

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    socket 多線(xiàn)程編程實(shí)現(xiàn)方法

    在現(xiàn)代網(wǎng)絡(luò)編程中,多線(xiàn)程技術(shù)被廣泛應(yīng)用于提高服務(wù)器的并發(fā)處理能力。Socket編程是網(wǎng)絡(luò)通信的基礎(chǔ),而將多線(xiàn)程技術(shù)應(yīng)用于Socket編程,可以顯著提升服務(wù)器的性能。 多線(xiàn)程編程的基本概
    的頭像 發(fā)表于 11-12 14:16 ?81次閱讀

    Python中多線(xiàn)程和多進(jìn)程的區(qū)別

    Python作為一種高級(jí)編程語(yǔ)言,提供了多種并發(fā)編程的方式,其中多線(xiàn)程與多進(jìn)程是最常見(jiàn)的兩種方式之一。在本文中,我們將探討Python中多線(xiàn)程與多進(jìn)程的概念、區(qū)別以及如何使用線(xiàn)程池與進(jìn)程池來(lái)提高并發(fā)執(zhí)行效率。
    的頭像 發(fā)表于 10-23 11:48 ?251次閱讀
    Python中<b class='flag-5'>多線(xiàn)程</b>和多進(jìn)程的區(qū)別

    多線(xiàn)程設(shè)計(jì)模式到對(duì) CompletableFuture 的應(yīng)用

    最近在開(kāi)發(fā) 延保服務(wù) 頻道頁(yè)時(shí),為了提高查詢(xún)效率,使用到了多線(xiàn)程技術(shù)。為了對(duì)多線(xiàn)程方案設(shè)計(jì)有更加充分的了解,在業(yè)余時(shí)間讀完了《圖解 Java 多線(xiàn)程設(shè)計(jì)模式》這本書(shū),覺(jué)得收獲良多。本篇文章將介紹其中
    的頭像 發(fā)表于 06-26 14:18 ?254次閱讀
    從<b class='flag-5'>多線(xiàn)程</b>設(shè)計(jì)模式到對(duì) CompletableFuture 的應(yīng)用

    鴻蒙OS開(kāi)發(fā)實(shí)例:【ArkTS類(lèi)庫(kù)多線(xiàn)程I/O密集型任務(wù)開(kāi)發(fā)】

    使用異步并發(fā)可以解決單次I/O任務(wù)阻塞的問(wèn)題,但是如果遇到I/O密集型任務(wù),同樣會(huì)阻塞線(xiàn)程中其它任務(wù)的執(zhí)行,這時(shí)需要使用多線(xiàn)程并發(fā)能力來(lái)進(jìn)行解決。 I/O密集型任務(wù)的性能重點(diǎn)通常不在于CPU
    的頭像 發(fā)表于 04-01 16:32 ?443次閱讀
    鴻蒙OS開(kāi)發(fā)實(shí)例:【ArkTS類(lèi)庫(kù)<b class='flag-5'>多線(xiàn)程</b>I/O密集型任務(wù)開(kāi)發(fā)】

    鴻蒙原生應(yīng)用開(kāi)發(fā)-ArkTS語(yǔ)言基礎(chǔ)類(lèi)庫(kù)多線(xiàn)程TaskPool和Worker的對(duì)比(一)

    TaskPool(任務(wù)池)和Worker的作用是為應(yīng)用程序提供一個(gè)多線(xiàn)程的運(yùn)行環(huán)境,用于處理耗時(shí)的計(jì)算任務(wù)或其他密集型任務(wù)。可以有效地避免這些任務(wù)阻塞主線(xiàn)程,從而最大化系統(tǒng)的利用率,降低整體資源消耗
    發(fā)表于 03-25 14:11

    鴻蒙原生應(yīng)用開(kāi)發(fā)-ArkTS語(yǔ)言基礎(chǔ)類(lèi)庫(kù)多線(xiàn)程I/O密集型任務(wù)開(kāi)發(fā)

    使用異步并發(fā)可以解決單次I/O任務(wù)阻塞的問(wèn)題,但是如果遇到I/O密集型任務(wù),同樣會(huì)阻塞線(xiàn)程中其它任務(wù)的執(zhí)行,這時(shí)需要使用多線(xiàn)程并發(fā)能力來(lái)進(jìn)行解決。 I/O密集型任務(wù)的性能重點(diǎn)通常不在于CPU的
    發(fā)表于 03-21 14:57

    java實(shí)現(xiàn)多線(xiàn)程的幾種方式

    Java實(shí)現(xiàn)多線(xiàn)程的幾種方式 多線(xiàn)程是指程序中包含了兩個(gè)或以上的線(xiàn)程,每個(gè)線(xiàn)程都可以并行執(zhí)行不同的任務(wù)或操作。Java中的多線(xiàn)程可以提高程序
    的頭像 發(fā)表于 03-14 16:55 ?551次閱讀

    AT socket可以多線(xiàn)程調(diào)用嗎?

    請(qǐng)問(wèn)AT socket 可以多線(xiàn)程調(diào)用嗎? 有互鎖機(jī)制嗎,還是要自己做互鎖。
    發(fā)表于 03-01 08:22

    redis多線(xiàn)程還能保證線(xiàn)程安全嗎

    Redis是一種使用C語(yǔ)言編寫(xiě)的高性能鍵值存儲(chǔ)系統(tǒng),它是單線(xiàn)程的,因?yàn)槭褂昧硕嗦窂?fù)用的方式來(lái)處理并發(fā)請(qǐng)求。這樣的實(shí)現(xiàn)方式帶來(lái)了很好的性能,但同時(shí)也引發(fā)了一些線(xiàn)程安全方面的問(wèn)題。 在Redis中,由于
    的頭像 發(fā)表于 12-05 10:28 ?1640次閱讀

    redis使用多線(xiàn)程處理操作命令

    Redis 是一個(gè)使用多線(xiàn)程處理操作命令的開(kāi)源內(nèi)存數(shù)據(jù)庫(kù)系統(tǒng)。它以其高性能、可擴(kuò)展性和靈活性而聞名,通常被用作緩存、消息代理和數(shù)據(jù)存儲(chǔ)等各種應(yīng)用場(chǎng)景。在本文中,我們將詳盡、詳實(shí)、細(xì)致地探
    的頭像 發(fā)表于 12-05 10:25 ?531次閱讀

    mfc多線(xiàn)程編程實(shí)例

    (圖形用戶(hù)界面)應(yīng)用程序的開(kāi)發(fā)。在這篇文章中,我們將重點(diǎn)介紹MFC中的多線(xiàn)程編程。 多線(xiàn)程編程在軟件開(kāi)發(fā)中非常重要,它可以實(shí)現(xiàn)程序的并發(fā)執(zhí)行,提高程序的效率和響應(yīng)速度。MFC提供了豐富的多線(xiàn)程支持,可以輕松地實(shí)現(xiàn)
    的頭像 發(fā)表于 12-01 14:29 ?1379次閱讀

    Spring Boot 3.2支持虛擬線(xiàn)程和原生鏡像

    Spring Boot 3.2 前幾日發(fā)布,讓我們用 Java 21、GraalVM 和虛擬線(xiàn)程來(lái)嘗試一下。
    的頭像 發(fā)表于 11-30 16:22 ?675次閱讀

    多線(xiàn)程如何保證數(shù)據(jù)的同步

    多線(xiàn)程編程是一種并發(fā)編程的方法,意味著程序中同時(shí)運(yùn)行多個(gè)線(xiàn)程,每個(gè)線(xiàn)程可獨(dú)立執(zhí)行不同的任務(wù),共享同一份數(shù)據(jù)。由于多線(xiàn)程并發(fā)執(zhí)行的特點(diǎn),會(huì)引發(fā)數(shù)據(jù)同步的問(wèn)題,即保證多個(gè)
    的頭像 發(fā)表于 11-17 14:22 ?1114次閱讀

    多線(xiàn)程并發(fā)查詢(xún)oracle數(shù)據(jù)庫(kù)

    多線(xiàn)程并發(fā)查詢(xún)Oracle數(shù)據(jù)庫(kù)是指在同一時(shí)間內(nèi)有多個(gè)線(xiàn)程同時(shí)執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)操作。這種并發(fā)查詢(xún)的方式可以提高系統(tǒng)的吞吐量和響應(yīng)速度,提高數(shù)據(jù)庫(kù)的效率和性能。本文將詳細(xì)介紹多線(xiàn)程并發(fā)查詢(xún)Oracle
    的頭像 發(fā)表于 11-17 14:22 ?3577次閱讀

    多線(xiàn)程同步的幾種方法

    多線(xiàn)程同步是指在多個(gè)線(xiàn)程并發(fā)執(zhí)行的情況下,為了保證線(xiàn)程執(zhí)行的正確性和一致性,需要采用特定的方法來(lái)協(xié)調(diào)線(xiàn)程之間的執(zhí)行順序和共享資源的訪(fǎng)問(wèn)。下面將介紹幾種常見(jiàn)的
    的頭像 發(fā)表于 11-17 14:16 ?1108次閱讀