伊人久久大香线蕉综合影视_日韩精品少妇无码受不了_71pao成人国产永久免费视频_国产伦片中文免费观看_国产高清无码麻豆精品_九色综合伊人久久富二代_日韩黄色精品_日韩A∨精品日韩精品无码

VUE-多文件斷點(diǎn)續(xù)傳、秒傳、分片上傳

2020-7-20    seo達(dá)人

凡是要知其然知其所以然

文件上傳相信很多朋友都有遇到過,那或許你也遇到過當(dāng)上傳大文件時(shí),上傳時(shí)間較長(zhǎng),且經(jīng)常失敗的困擾,并且失敗后,又得重新上傳很是煩人。那我們先了解下失敗的原因吧!


據(jù)我了解大概有以下原因:


服務(wù)器配置:例如在PHP中默認(rèn)的文件上傳大小為8M【post_max_size = 8m】,若你在一個(gè)請(qǐng)求體中放入8M以上的內(nèi)容時(shí),便會(huì)出現(xiàn)異常

請(qǐng)求超時(shí):當(dāng)你設(shè)置了接口的超時(shí)時(shí)間為10s,那么上傳大文件時(shí),一個(gè)接口響應(yīng)時(shí)間超過10s,那么便會(huì)被Faild掉。

網(wǎng)絡(luò)波動(dòng):這個(gè)就屬于不可控因素,也是較常見的問題。

基于以上原因,聰明的人們就想到了,將文件拆分多個(gè)小文件,依次上傳,不就解決以上1,2問題嘛,這便是分片上傳。 網(wǎng)絡(luò)波動(dòng)這個(gè)實(shí)在不可控,也許一陣大風(fēng)刮來,就斷網(wǎng)了呢。那這樣好了,既然斷網(wǎng)無法控制,那我可以控制只上傳已經(jīng)上傳的文件內(nèi)容,不就好了,這樣大大加快了重新上傳的速度。所以便有了“斷點(diǎn)續(xù)傳”一說。此時(shí),人群中有人插了一嘴,有些文件我已經(jīng)上傳一遍了,為啥還要在上傳,能不能不浪費(fèi)我流量和時(shí)間。喔...這個(gè)嘛,簡(jiǎn)單,每次上傳時(shí)判斷下是否存在這個(gè)文件,若存在就不重新上傳便可,于是又有了“秒傳”一說。從此這"三兄弟" 便自行CP,統(tǒng)治了整個(gè)文件界。”

注意文中的代碼并非實(shí)際代碼,請(qǐng)移步至github查看代碼

https://github.com/pseudo-god...


分片上傳

HTML

原生INPUT樣式較丑,這里通過樣式疊加的方式,放一個(gè)Button.

 <div class="btns">

   <el-button-group>

     <el-button :disabled="changeDisabled">

       <i class="el-icon-upload2 el-icon--left" size="mini"></i>選擇文件

       <input

         v-if="!changeDisabled"

         type="file"

         :multiple="multiple"

         class="select-file-input"

         :accept="accept"

         @change="handleFileChange"

       />

     </el-button>

     <el-button :disabled="uploadDisabled" @click="handleUpload()"><i class="el-icon-upload el-icon--left" size="mini"></i>上傳</el-button>

     <el-button :disabled="pauseDisabled" @click="handlePause"><i class="el-icon-video-pause el-icon--left" size="mini"></i>暫停</el-button>

     <el-button :disabled="resumeDisabled" @click="handleResume"><i class="el-icon-video-play el-icon--left" size="mini"></i>恢復(fù)</el-button>

     <el-button :disabled="clearDisabled" @click="clearFiles"><i class="el-icon-video-play el-icon--left" size="mini"></i>清空</el-button>

   </el-button-group>

   <slot

   

//data 數(shù)據(jù)


var chunkSize = 10 * 1024 * 1024; // 切片大小

var fileIndex = 0; // 當(dāng)前正在被遍歷的文件下標(biāo)


data: () => ({

   container: {

     files: null

   },

   tempFilesArr: [], // 存儲(chǔ)files信息

   cancels: [], // 存儲(chǔ)要取消的請(qǐng)求

   tempThreads: 3,

   // 默認(rèn)狀態(tài)

   status: Status.wait

 }),

   

一個(gè)稍微好看的UI就出來了。




選擇文件

選擇文件過程中,需要對(duì)外暴露出幾個(gè)鉤子,熟悉elementUi的同學(xué)應(yīng)該很眼熟,這幾個(gè)鉤子基本與其一致。onExceed:文件超出個(gè)數(shù)限制時(shí)的鉤子、beforeUpload:文件上傳之前

fileIndex 這個(gè)很重要,因?yàn)槭嵌辔募蟼鳎远ㄎ划?dāng)前正在被上傳的文件就很重要,基本都靠它


handleFileChange(e) {

 const files = e.target.files;

 if (!files) return;

 Object.assign(this.$data, this.$options.data()); // 重置data所有數(shù)據(jù)


 fileIndex = 0; // 重置文件下標(biāo)

 this.container.files = files;

 // 判斷文件選擇的個(gè)數(shù)

 if (this.limit && this.container.files.length > this.limit) {

   this.onExceed && this.onExceed(files);

   return;

 }


 // 因filelist不可編輯,故拷貝filelist 對(duì)象

 var index = 0; // 所選文件的下標(biāo),主要用于剔除文件后,原文件list與臨時(shí)文件list不對(duì)應(yīng)的情況

 for (const key in this.container.files) {

   if (this.container.files.hasOwnProperty(key)) {

     const file = this.container.files[key];


     if (this.beforeUpload) {

       const before = this.beforeUpload(file);

       if (before) {

         this.pushTempFile(file, index);

       }

     }


     if (!this.beforeUpload) {

       this.pushTempFile(file, index);

     }


     index++;

   }

 }

},

// 存入 tempFilesArr,為了上面的鉤子,所以將代碼做了拆分

pushTempFile(file, index) {

 // 額外的初始值

 const obj = {

   status: fileStatus.wait,

   chunkList: [],

   uploadProgress: 0,

   hashProgress: 0,

   index

 };

 for (const k in file) {

   obj[k] = file[k];

 }

 console.log('pushTempFile -> obj', obj);

 this.tempFilesArr.push(obj);

}

分片上傳

創(chuàng)建切片,循環(huán)分解文件即可


 createFileChunk(file, size = chunkSize) {

   const fileChunkList = [];

   var count = 0;

   while (count < file.size) {

     fileChunkList.push({

       file: file.slice(count, count + size)

     });

     count += size;

   }

   return fileChunkList;

 }

循環(huán)創(chuàng)建切片,既然咱們做的是多文件,所以這里就有循環(huán)去處理,依次創(chuàng)建文件切片,及切片的上傳。

async handleUpload(resume) {

 if (!this.container.files) return;

 this.status = Status.uploading;

 const filesArr = this.container.files;

 var tempFilesArr = this.tempFilesArr;


 for (let i = 0; i < tempFilesArr.length; i++) {

   fileIndex = i;

   //創(chuàng)建切片

   const fileChunkList = this.createFileChunk(

     filesArr[tempFilesArr[i].index]

   );

     

   tempFilesArr[i].fileHash ='xxxx'; // 先不用看這個(gè),后面會(huì)講,占個(gè)位置

   tempFilesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({

     fileHash: tempFilesArr[i].hash,

     fileName: tempFilesArr[i].name,

     index,

     hash: tempFilesArr[i].hash + '-' + index,

     chunk: file,

     size: file.size,

     uploaded: false,

     progress: 0, // 每個(gè)塊的上傳進(jìn)度

     status: 'wait' // 上傳狀態(tài),用作進(jìn)度狀態(tài)顯示

   }));

   

   //上傳切片

   await this.uploadChunks(this.tempFilesArr[i]);

 }

}

上傳切片,這個(gè)里需要考慮的問題較多,也算是核心吧,uploadChunks方法只負(fù)責(zé)構(gòu)造傳遞給后端的數(shù)據(jù),核心上傳功能放到sendRequest方法中

async uploadChunks(data) {

 var chunkData = data.chunkList;

 const requestDataList = chunkData

   .map(({ fileHash, chunk, fileName, index }) => {

     const formData = new FormData();

     formData.append('md5', fileHash);

     formData.append('file', chunk);

     formData.append('fileName', index); // 文件名使用切片的下標(biāo)

     return { formData, index, fileName };

   });


 try {

   await this.sendRequest(requestDataList, chunkData);

 } catch (error) {

   // 上傳有被reject的

   this.$message.error('親 上傳失敗了,考慮重試下呦' + error);

   return;

 }


 // 合并切片

 const isUpload = chunkData.some(item => item.uploaded === false);

 console.log('created -> isUpload', isUpload);

 if (isUpload) {

   alert('存在失敗的切片');

 } else {

   // 執(zhí)行合并

   await this.mergeRequest(data);

 }

}

sendReques。上傳這是最重要的地方,也是容易失敗的地方,假設(shè)有10個(gè)分片,那我們?nèi)羰侵苯影l(fā)10個(gè)請(qǐng)求的話,很容易達(dá)到瀏覽器的瓶頸,所以需要對(duì)請(qǐng)求進(jìn)行并發(fā)處理。


并發(fā)處理:這里我使用for循環(huán)控制并發(fā)的初始并發(fā)數(shù),然后在 handler 函數(shù)里調(diào)用自己,這樣就控制了并發(fā)。在handler中,通過數(shù)組API.shift模擬隊(duì)列的效果,來上傳切片。

重試: retryArr 數(shù)組存儲(chǔ)每個(gè)切片文件請(qǐng)求的重試次數(shù),做累加。比如[1,0,2],就是第0個(gè)文件切片報(bào)錯(cuò)1次,第2個(gè)報(bào)錯(cuò)2次。為保證能與文件做對(duì)應(yīng),const index = formInfo.index; 我們直接從數(shù)據(jù)中拿之前定義好的index。 若失敗后,將失敗的請(qǐng)求重新加入隊(duì)列即可。


關(guān)于并發(fā)及重試我寫了一個(gè)小Demo,若不理解可以自己在研究下,文件地址:https://github.com/pseudo-god... , 重試代碼好像被我弄丟了,大家要是有需求,我再補(bǔ)吧!

   // 并發(fā)處理

sendRequest(forms, chunkData) {

 var finished = 0;

 const total = forms.length;

 const that = this;

 const retryArr = []; // 數(shù)組存儲(chǔ)每個(gè)文件hash請(qǐng)求的重試次數(shù),做累加 比如[1,0,2],就是第0個(gè)文件切片報(bào)錯(cuò)1次,第2個(gè)報(bào)錯(cuò)2次


 return new Promise((resolve, reject) => {

   const handler = () => {

     if (forms.length) {

       // 出棧

       const formInfo = forms.shift();


       const formData = formInfo.formData;

       const index = formInfo.index;

       

       instance.post('fileChunk', formData, {

         onUploadProgress: that.createProgresshandler(chunkData[index]),

         cancelToken: new CancelToken(c => this.cancels.push(c)),

         timeout: 0

       }).then(res => {

         console.log('handler -> res', res);

         // 更改狀態(tài)

         chunkData[index].uploaded = true;

         chunkData[index].status = 'success';

         

         finished++;

         handler();

       })

         .catch(e => {

           // 若暫停,則禁止重試

           if (this.status === Status.pause) return;

           if (typeof retryArr[index] !== 'number') {

             retryArr[index] = 0;

           }


           // 更新狀態(tài)

           chunkData[index].status = 'warning';


           // 累加錯(cuò)誤次數(shù)

           retryArr[index]++;


           // 重試3次

           if (retryArr[index] >= this.chunkRetry) {

             return reject('重試失敗', retryArr);

           }


           this.tempThreads++; // 釋放當(dāng)前占用的通道


           // 將失敗的重新加入隊(duì)列

           forms.push(formInfo);

           handler();

         });

     }


     if (finished >= total) {

       resolve('done');

     }

   };


   // 控制并發(fā)

   for (let i = 0; i < this.tempThreads; i++) {

     handler();

   }

 });

}

切片的上傳進(jìn)度,通過axios的onUploadProgress事件,結(jié)合createProgresshandler方法進(jìn)行維護(hù)

// 切片上傳進(jìn)度

createProgresshandler(item) {

 return p => {

   item.progress = parseInt(String((p.loaded / p.total) * 100));

   this.fileProgress();

 };

}

Hash計(jì)算

其實(shí)就是算一個(gè)文件的MD5值,MD5在整個(gè)項(xiàng)目中用到的地方也就幾點(diǎn)。

秒傳,需要通過MD5值判斷文件是否已存在。

續(xù)傳:需要用到MD5作為key值,當(dāng)唯一值使用。

本項(xiàng)目主要使用worker處理,性能及速度都會(huì)有很大提升.

由于是多文件,所以HASH的計(jì)算進(jìn)度也要體現(xiàn)在每個(gè)文件上,所以這里使用全局變量fileIndex來定位當(dāng)前正在被上傳的文件

執(zhí)行計(jì)算hash


正在上傳文件


// 生成文件 hash(web-worker)

calculateHash(fileChunkList) {

 return new Promise(resolve => {

   this.container.worker = new Worker('./hash.js');

   this.container.worker.postMessage({ fileChunkList });

   this.container.worker.onmessage = e => {

     const { percentage, hash } = e.data;

     if (this.tempFilesArr[fileIndex]) {

       this.tempFilesArr[fileIndex].hashProgress = Number(

         percentage.toFixed(0)

       );

     }


     if (hash) {

       resolve(hash);

     }

   };

 });

}

因使用worker,所以我們不能直接使用NPM包方式使用MD5。需要單獨(dú)去下載spark-md5.js文件,并引入


//hash.js


self.importScripts("/spark-md5.min.js"); // 導(dǎo)入腳本

// 生成文件 hash

self.onmessage = e => {

 const { fileChunkList } = e.data;

 const spark = new self.SparkMD5.ArrayBuffer();

 let percentage = 0;

 let count = 0;

 const loadNext = index => {

   const reader = new FileReader();

   reader.readAsArrayBuffer(fileChunkList[index].file);

   reader.onload = e => {

     count++;

     spark.append(e.target.result);

     if (count === fileChunkList.length) {

       self.postMessage({

         percentage: 100,

         hash: spark.end()

       });

       self.close();

     } else {

       percentage += 100 / fileChunkList.length;

       self.postMessage({

         percentage

       });

       loadNext(count);

     }

   };

 };

 loadNext(0);

};

文件合并

當(dāng)我們的切片全部上傳完畢后,就需要進(jìn)行文件的合并,這里我們只需要請(qǐng)求接口即可

mergeRequest(data) {

  const obj = {

    md5: data.fileHash,

    fileName: data.name,

    fileChunkNum: data.chunkList.length

  };


  instance.post('fileChunk/merge', obj,

    {

      timeout: 0

    })

    .then((res) => {

      this.$message.success('上傳成功');

    });

}

Done: 至此一個(gè)分片上傳的功能便已完成

斷點(diǎn)續(xù)傳

顧名思義,就是從那斷的就從那開始,明確思路就很簡(jiǎn)單了。一般有2種方式,一種為服務(wù)器端返回,告知我從那開始,還有一種是瀏覽器端自行處理。2種方案各有優(yōu)缺點(diǎn)。本項(xiàng)目使用第二種。

思路:已文件HASH為key值,每個(gè)切片上傳成功后,記錄下來便可。若需要續(xù)傳時(shí),直接跳過記錄中已存在的便可。本項(xiàng)目將使用Localstorage進(jìn)行存儲(chǔ),這里我已提前封裝好addChunkStorage、getChunkStorage方法。


存儲(chǔ)在Stroage的數(shù)據(jù)




緩存處理

在切片上傳的axios成功回調(diào)中,存儲(chǔ)已上傳成功的切片


instance.post('fileChunk', formData, )

 .then(res => {

   // 存儲(chǔ)已上傳的切片下標(biāo)

+ this.addChunkStorage(chunkData[index].fileHash, index);

   handler();

 })

在切片上傳前,先看下localstorage中是否存在已上傳的切片,并修改uploaded


   async handleUpload(resume) {

+      const getChunkStorage = this.getChunkStorage(tempFilesArr[i].hash);

     tempFilesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({

+        uploaded: getChunkStorage && getChunkStorage.includes(index), // 標(biāo)識(shí):是否已完成上傳

+        progress: getChunkStorage && getChunkStorage.includes(index) ? 100 : 0,

+        status: getChunkStorage && getChunkStorage.includes(index)? 'success'

+              : 'wait' // 上傳狀態(tài),用作進(jìn)度狀態(tài)顯示

     }));


   }

構(gòu)造切片數(shù)據(jù)時(shí),過濾掉uploaded為true的


async uploadChunks(data) {

 var chunkData = data.chunkList;

 const requestDataList = chunkData

+    .filter(({ uploaded }) => !uploaded)

   .map(({ fileHash, chunk, fileName, index }) => {

     const formData = new FormData();

     formData.append('md5', fileHash);

     formData.append('file', chunk);

     formData.append('fileName', index); // 文件名使用切片的下標(biāo)

     return { formData, index, fileName };

   })

}

垃圾文件清理

隨著上傳文件的增多,相應(yīng)的垃圾文件也會(huì)增多,比如有些時(shí)候上傳一半就不再繼續(xù),或上傳失敗,碎片文件就會(huì)增多。解決方案我目前想了2種

前端在localstorage設(shè)置緩存時(shí)間,超過時(shí)間就發(fā)送請(qǐng)求通知后端清理碎片文件,同時(shí)前端也要清理緩存。

前后端都約定好,每個(gè)緩存從生成開始,只能存儲(chǔ)12小時(shí),12小時(shí)后自動(dòng)清理

以上2中方案似乎都有點(diǎn)問題,極有可能造成前后端因時(shí)間差,引發(fā)切片上傳異常的問題,后面想到合適的解決方案再來更新吧。

Done: 續(xù)傳到這里也就完成了。


秒傳

這算是最簡(jiǎn)單的,只是聽起來很厲害的樣子。原理:計(jì)算整個(gè)文件的HASH,在執(zhí)行上傳操作前,向服務(wù)端發(fā)送請(qǐng)求,傳遞MD5值,后端進(jìn)行文件檢索。若服務(wù)器中已存在該文件,便不進(jìn)行后續(xù)的任何操作,上傳也便直接結(jié)束。大家一看就明白

async handleUpload(resume) {

   if (!this.container.files) return;

   const filesArr = this.container.files;

   var tempFilesArr = this.tempFilesArr;


   for (let i = 0; i < tempFilesArr.length; i++) {

     const fileChunkList = this.createFileChunk(

       filesArr[tempFilesArr[i].index]

     );


     // hash校驗(yàn),是否為秒傳

+      tempFilesArr[i].hash = await this.calculateHash(fileChunkList);

+      const verifyRes = await this.verifyUpload(

+        tempFilesArr[i].name,

+        tempFilesArr[i].hash

+      );

+      if (verifyRes.data.presence) {

+       tempFilesArr[i].status = fileStatus.secondPass;

+       tempFilesArr[i].uploadProgress = 100;

+      } else {

       console.log('開始上傳切片文件----》', tempFilesArr[i].name);

       await this.uploadChunks(this.tempFilesArr[i]);

     }

   }

 }

 // 文件上傳之前的校驗(yàn): 校驗(yàn)文件是否已存在

 verifyUpload(fileName, fileHash) {

   return new Promise(resolve => {

     const obj = {

       md5: fileHash,

       fileName,

       ...this.uploadArguments //傳遞其他參數(shù)

     };

     instance

       .post('fileChunk/presence', obj)

       .then(res => {

         resolve(res.data);

       })

       .catch(err => {

         console.log('verifyUpload -> err', err);

       });

   });

 }

Done: 秒傳到這里也就完成了。

后端處理

文章好像有點(diǎn)長(zhǎng)了,具體代碼邏輯就先不貼了,除非有人留言要求,嘻嘻,有時(shí)間再更新

Node版

請(qǐng)前往 https://github.com/pseudo-god... 查看

JAVA版

下周應(yīng)該會(huì)更新處理

PHP版

1年多沒寫PHP了,抽空我會(huì)慢慢補(bǔ)上來

待完善

切片的大?。哼@個(gè)后面會(huì)做出動(dòng)態(tài)計(jì)算的。需要根據(jù)當(dāng)前所上傳文件的大小,自動(dòng)計(jì)算合適的切片大小。避免出現(xiàn)切片過多的情況。

文件追加:目前上傳文件過程中,不能繼續(xù)選擇文件加入隊(duì)列。(這個(gè)沒想好應(yīng)該怎么處理。)

更新記錄

組件已經(jīng)運(yùn)行一段時(shí)間了,期間也測(cè)試出幾個(gè)問題,本來以為沒BUG的,看起來BUG都挺嚴(yán)重

BUG-1:當(dāng)同時(shí)上傳多個(gè)內(nèi)容相同但是文件名稱不同的文件時(shí),出現(xiàn)上傳失敗的問題。


預(yù)期結(jié)果:第一個(gè)上傳成功后,后面相同的問文件應(yīng)該直接秒傳


實(shí)際結(jié)果:第一個(gè)上傳成功后,其余相同的文件都失敗,錯(cuò)誤信息,塊數(shù)不對(duì)。


原因:當(dāng)?shù)谝粋€(gè)文件塊上傳完畢后,便立即進(jìn)行了下一個(gè)文件的循環(huán),導(dǎo)致無法及時(shí)獲取文件是否已秒傳的狀態(tài),從而導(dǎo)致失敗。


解決方案:在當(dāng)前文件分片上傳完畢并且請(qǐng)求合并接口完畢后,再進(jìn)行下一次循環(huán)。


將子方法都改為同步方式,mergeRequest 和 uploadChunks 方法





BUG-2: 當(dāng)每次選擇相同的文件并觸發(fā)beforeUpload方法時(shí),若第二次也選擇了相同的文件,beforeUpload方法失效,從而導(dǎo)致整個(gè)流程失效。

原因:之前每次選擇文件時(shí),沒有清空上次所選input文件的數(shù)據(jù),相同數(shù)據(jù)的情況下,是不會(huì)觸發(fā)input的change事件。


解決方案:每次點(diǎn)擊input時(shí),清空數(shù)據(jù)即可。我順帶優(yōu)化了下其他的代碼,具體看提交記錄吧。


<input

 v-if="!changeDisabled"

 type="file"

 :multiple="multiple"

 class="select-file-input"

 :accept="accept"

+  οnclick="f.outerHTML=f.outerHTML"

 @change="handleFileChange"/>

重寫了暫停和恢復(fù)的功能,實(shí)際上,主要是增加了暫停和恢復(fù)的狀態(tài)





之前的處理邏輯太簡(jiǎn)單粗暴,存在諸多問題?,F(xiàn)在將狀態(tài)定位在每一個(gè)文件之上,這樣恢復(fù)上傳時(shí),直接跳過即可





封裝組件

寫了一大堆,其實(shí)以上代碼你直接復(fù)制也無法使用,這里我將此封裝了一個(gè)組件。大家可以去github下載文件,里面有使用案例 ,若有用記得隨手給個(gè)star,謝謝!

偷個(gè)懶,具體封裝組件的代碼就不列出來了,大家直接去下載文件查看,若有不明白的,可留言。


組件文檔

Attribute

參數(shù) 類型 說明 默認(rèn) 備注

headers Object 設(shè)置請(qǐng)求頭

before-upload Function 上傳文件前的鉤子,返回false則停止上傳

accept String 接受上傳的文件類型

upload-arguments Object 上傳文件時(shí)攜帶的參數(shù)

with-credentials Boolean 是否傳遞Cookie false

limit Number 最大允許上傳個(gè)數(shù) 0 0為不限制

on-exceed Function 文件超出個(gè)數(shù)限制時(shí)的鉤子

multiple Boolean 是否為多選模式 true

base-url String 由于本組件為內(nèi)置的AXIOS,若你需要走代理,可以直接在這里配置你的基礎(chǔ)路徑

chunk-size Number 每個(gè)切片的大小 10M

threads Number 請(qǐng)求的并發(fā)數(shù) 3 并發(fā)數(shù)越高,對(duì)服務(wù)器的性能要求越高,盡可能用默認(rèn)值即可

chunk-retry Number 錯(cuò)誤重試次數(shù) 3 分片請(qǐng)求的錯(cuò)誤重試次數(shù)

Slot

方法名 說明 參數(shù) 備注

header 按鈕區(qū)域 無

tip 提示說明文字 無

后端接口文檔:按文檔實(shí)現(xiàn)即可

藍(lán)藍(lán)設(shè)計(jì)www.cqzjtgb.com )是一家專注而深入的界面設(shè)計(jì)公司,為期望卓越的國內(nèi)外企業(yè)提供卓越的UI界面設(shè)計(jì)、BS界面設(shè)計(jì) 、 cs界面設(shè)計(jì) 、 ipad界面設(shè)計(jì) 、 包裝設(shè)計(jì) 、 圖標(biāo)定制 、 用戶體驗(yàn) 、交互設(shè)計(jì)、 網(wǎng)站建設(shè) 、平面設(shè)計(jì)服務(wù)






日歷

鏈接

個(gè)人資料

存檔

中文字幕人妻丝袜制服| av电影中文网址| 久久久久久久精品精品| 日本猛色少妇xxxxx猛交久久| 久久综合国产亚洲精品| 熟女少妇亚洲综合色aaa.| 亚洲av电影在线进入| 亚洲久久久国产精品| 久久久久精品人妻al黑| 伦理电影大哥的女人| 美女脱内裤让男人舔精品视频| 国产精品久久久久久人妻精品电影 | 老熟女久久久| 国产精品久久久av美女十八| 午夜免费男女啪啪视频观看| 亚洲中文av在线| 亚洲精品aⅴ在线观看| 欧美日韩视频精品一区| 精品一区二区三区四区五区乱码 | 又大又黄又爽视频免费| 日韩中文字幕视频在线看片| 国产人伦9x9x在线观看| 国产av国产精品国产| 老司机影院成人| 国产精品国产三级国产专区5o| 熟妇人妻不卡中文字幕| 久久狼人影院| 黄片小视频在线播放| 夫妻性生交免费视频一级片| 亚洲精品aⅴ在线观看| 免费在线观看黄色视频的| 熟女少妇亚洲综合色aaa.| 日日摸夜夜添夜夜爱| 在线 av 中文字幕| 国产精品久久久久久精品古装| 91成人精品电影| 一区二区日韩欧美中文字幕| 亚洲,欧美,日韩| 黑丝袜美女国产一区| 一级片免费观看大全| av福利片在线| xxx大片免费视频| 一区二区三区四区激情视频| av不卡在线播放| 如何舔出高潮| 一级爰片在线观看| 在线精品无人区一区二区三| 国产精品av久久久久免费| 视频在线观看一区二区三区| 国产麻豆69| 精品一区在线观看国产| 热re99久久国产66热| 国产亚洲av高清不卡| 伊人久久国产一区二区| 国产精品国产av在线观看| 国产精品一区二区在线观看99| 这个男人来自地球电影免费观看 | 亚洲国产精品国产精品| 一区二区日韩欧美中文字幕| 亚洲人成77777在线视频| 又大又爽又粗| 日韩一卡2卡3卡4卡2021年| 国产视频首页在线观看| 99久久99久久久精品蜜桃| 少妇人妻精品综合一区二区| 亚洲专区中文字幕在线 | 黑人猛操日本美女一级片| 毛片一级片免费看久久久久| 一边摸一边抽搐一进一出视频| 在线亚洲精品国产二区图片欧美| 午夜影院在线不卡| 国产精品二区激情视频| 99精国产麻豆久久婷婷| 亚洲av中文av极速乱| 精品一区二区三区四区五区乱码 | 国产成人欧美在线观看 | 自线自在国产av| 在线观看国产h片| av卡一久久| 曰老女人黄片| 亚洲综合精品二区| 欧美日韩精品网址| 18禁动态无遮挡网站| 亚洲国产精品国产精品| 亚洲第一青青草原| 黑丝袜美女国产一区| 国产精品成人在线| av网站在线播放免费| 丁香六月欧美| 在线免费观看不下载黄p国产| 精品亚洲乱码少妇综合久久| 亚洲第一青青草原| 高清黄色对白视频在线免费看| 99热国产这里只有精品6| 午夜福利乱码中文字幕| 国产一区二区激情短视频 | 黄色视频在线播放观看不卡| 丝瓜视频免费看黄片| 黄片小视频在线播放| 最近最新中文字幕大全免费视频 | 国产男女内射视频| 精品少妇内射三级| 亚洲精品第二区| 久久久国产欧美日韩av| 看免费成人av毛片| 亚洲精品自拍成人| 国产97色在线日韩免费| 日日爽夜夜爽网站| 亚洲五月色婷婷综合| 在线观看人妻少妇| 国产一区二区三区综合在线观看| 久久久久久久久久久久大奶| 中国三级夫妇交换| 欧美国产精品va在线观看不卡| 国产精品99久久99久久久不卡 | 国产国语露脸激情在线看| 男女国产视频网站| 少妇的丰满在线观看| 波多野结衣一区麻豆| 亚洲精品久久久久久婷婷小说| 国产日韩欧美在线精品| 午夜免费男女啪啪视频观看| 欧美在线一区亚洲| 国产精品人妻久久久影院| 国产成人a∨麻豆精品| 老汉色∧v一级毛片| 亚洲国产日韩一区二区| 国产 一区精品| 成人影院久久| 久久久久久人妻| 波野结衣二区三区在线| 国产精品免费大片| 国产亚洲一区二区精品| 亚洲国产日韩一区二区| 可以免费在线观看a视频的电影网站 | 男人爽女人下面视频在线观看| 美女午夜性视频免费| 中文字幕人妻丝袜制服| www.熟女人妻精品国产| 国产欧美日韩一区二区三区在线| 精品国产露脸久久av麻豆| 性少妇av在线| 欧美日韩av久久| 赤兔流量卡办理| 91国产中文字幕| 看免费av毛片| 天天影视国产精品| 亚洲精品,欧美精品| 久久人人爽av亚洲精品天堂| 亚洲伊人色综图| av.在线天堂| 国产精品三级大全| 欧美 日韩 精品 国产| 女性生殖器流出的白浆| 亚洲国产欧美在线一区| 日韩中文字幕欧美一区二区 | 久久性视频一级片| 美女福利国产在线| 日本欧美视频一区| 免费人妻精品一区二区三区视频| 男男h啪啪无遮挡| 高清在线视频一区二区三区| 久久久久久久久久久久大奶| 啦啦啦在线免费观看视频4| 乱人伦中国视频| 精品国产露脸久久av麻豆| 自拍欧美九色日韩亚洲蝌蚪91| 无限看片的www在线观看| 国产色婷婷99| 亚洲一区二区三区欧美精品| av在线播放精品| 久久精品国产a三级三级三级| 黑丝袜美女国产一区| 各种免费的搞黄视频| 丝袜脚勾引网站| 久久久欧美国产精品| 欧美精品一区二区免费开放| 99九九在线精品视频| 日本欧美视频一区| 国产人伦9x9x在线观看| 亚洲专区中文字幕在线 | 黄色视频在线播放观看不卡| 不卡视频在线观看欧美| 中国三级夫妇交换| 黄网站色视频无遮挡免费观看| 日韩,欧美,国产一区二区三区| 亚洲美女视频黄频| 97人妻天天添夜夜摸| 午夜福利,免费看| 亚洲美女视频黄频| 一级黄片播放器| 黄色视频不卡| 亚洲国产中文字幕在线视频| 欧美成人精品欧美一级黄| av有码第一页| 一级毛片电影观看| 亚洲国产精品国产精品| 国产极品天堂在线| 亚洲综合精品二区| 深夜精品福利| 一级片'在线观看视频| 亚洲精品成人av观看孕妇| 99re6热这里在线精品视频| 国产一区二区在线观看av| www日本在线高清视频| 亚洲成人国产一区在线观看 | 国产1区2区3区精品| 免费黄频网站在线观看国产| 成人手机av| 精品卡一卡二卡四卡免费| 巨乳人妻的诱惑在线观看| 超色免费av| 国产成人a∨麻豆精品| 男女无遮挡免费网站观看| 两性夫妻黄色片| 王馨瑶露胸无遮挡在线观看| 午夜免费男女啪啪视频观看| 久久久久视频综合| 久久精品久久精品一区二区三区| 啦啦啦视频在线资源免费观看| 五月天丁香电影| 欧美黄色片欧美黄色片| 久久久久视频综合| 色综合欧美亚洲国产小说| 免费不卡黄色视频| 午夜av观看不卡| 黄频高清免费视频| 欧美激情极品国产一区二区三区| 亚洲精品久久成人aⅴ小说| 国产色婷婷99| 久久毛片免费看一区二区三区| 亚洲人成网站在线观看播放| 日韩欧美一区视频在线观看| 国产不卡av网站在线观看| 国产伦理片在线播放av一区| 国产高清不卡午夜福利| 交换朋友夫妻互换小说| 这个男人来自地球电影免费观看 | 久热爱精品视频在线9| 国产伦理片在线播放av一区| 久久久国产一区二区| 欧美成人精品欧美一级黄| 最近最新中文字幕大全免费视频 | 欧美 日韩 精品 国产| 高清黄色对白视频在线免费看| 久久久久久久精品精品| 久久精品aⅴ一区二区三区四区| 观看av在线不卡| 一边摸一边抽搐一进一出视频| 啦啦啦 在线观看视频| 777米奇影视久久| 蜜桃国产av成人99| 天天躁夜夜躁狠狠久久av| 91精品国产国语对白视频| 欧美97在线视频| 国产熟女午夜一区二区三区| 国产黄色视频一区二区在线观看| 最近中文字幕2019免费版| 国产片内射在线| 男女午夜视频在线观看| av免费观看日本| 欧美人与性动交α欧美软件| 亚洲成色77777| 国产精品欧美亚洲77777| 久久人人97超碰香蕉20202| 日韩一本色道免费dvd| 精品国产国语对白av| 欧美老熟妇乱子伦牲交| 高清av免费在线| 三上悠亚av全集在线观看| a级毛片在线看网站| 精品国产国语对白av| 免费在线观看完整版高清| 国产成人欧美在线观看 | 在线天堂中文资源库| 亚洲国产中文字幕在线视频| 欧美人与性动交α欧美软件| 中文字幕制服av| 人成视频在线观看免费观看| 一本久久精品| videosex国产| 日本爱情动作片www.在线观看| 久久国产精品男人的天堂亚洲| 美女主播在线视频| 麻豆精品久久久久久蜜桃| 中文字幕另类日韩欧美亚洲嫩草| xxxhd国产人妻xxx| 麻豆乱淫一区二区| 丁香六月欧美| 亚洲美女黄色视频免费看| 人成视频在线观看免费观看| 一级片免费观看大全| 日日爽夜夜爽网站| 一本一本久久a久久精品综合妖精| 一本色道久久久久久精品综合| 亚洲精品在线美女| 伊人亚洲综合成人网| 一二三四中文在线观看免费高清| 亚洲成人国产一区在线观看 | 18在线观看网站| 一本久久精品| 久久久欧美国产精品| 日本色播在线视频| 亚洲五月色婷婷综合| 99国产综合亚洲精品| 亚洲情色 制服丝袜| 国产一区有黄有色的免费视频| 婷婷色综合大香蕉| 亚洲精品在线美女| 这个男人来自地球电影免费观看 | 日本午夜av视频| 一级a爱视频在线免费观看| 久久人妻熟女aⅴ| av天堂久久9| 青春草视频在线免费观看| 精品久久久久久电影网| 国产亚洲av高清不卡| 永久免费av网站大全| av国产久精品久网站免费入址| 亚洲久久久国产精品| 夫妻午夜视频| 日韩欧美一区视频在线观看| 国产精品免费大片| 久久狼人影院| 少妇人妻久久综合中文| 男人舔女人的私密视频| 欧美在线一区亚洲| 2018国产大陆天天弄谢| 国产亚洲欧美精品永久| 一本色道久久久久久精品综合| 最近的中文字幕免费完整| 精品少妇黑人巨大在线播放| 一本色道久久久久久精品综合| 亚洲,一卡二卡三卡| 女人高潮潮喷娇喘18禁视频| 国产av码专区亚洲av| 日韩中文字幕视频在线看片| 国产无遮挡羞羞视频在线观看| 伦理电影大哥的女人| 亚洲免费av在线视频| 丰满少妇做爰视频| 日韩精品免费视频一区二区三区| 哪个播放器可以免费观看大片| 在线观看国产h片| 亚洲av成人不卡在线观看播放网 | 午夜日韩欧美国产| 免费黄色在线免费观看| 久久精品熟女亚洲av麻豆精品| 国产精品国产三级专区第一集| 国产一区二区 视频在线| 97人妻天天添夜夜摸| 色网站视频免费| 成人18禁高潮啪啪吃奶动态图| 老司机靠b影院| 国产在线免费精品| 十分钟在线观看高清视频www| 婷婷色av中文字幕| 欧美激情高清一区二区三区 | 国产成人午夜福利电影在线观看| av免费观看日本| 欧美乱码精品一区二区三区| 精品亚洲成国产av| 亚洲精品久久久久久婷婷小说| 最近中文字幕高清免费大全6| 国产深夜福利视频在线观看| 母亲3免费完整高清在线观看| 亚洲精品久久午夜乱码| 免费黄频网站在线观看国产| 亚洲av中文av极速乱| 啦啦啦视频在线资源免费观看| 国产色婷婷99| 一区在线观看完整版| 女人爽到高潮嗷嗷叫在线视频| 午夜免费男女啪啪视频观看| 亚洲av成人精品一二三区| 水蜜桃什么品种好| 在线天堂中文资源库| 国产亚洲午夜精品一区二区久久| 在线亚洲精品国产二区图片欧美| 美女大奶头黄色视频| 国产免费一区二区三区四区乱码| 亚洲人成网站在线观看播放| 日本vs欧美在线观看视频| 精品国产超薄肉色丝袜足j| 国产av码专区亚洲av| 国产成人av激情在线播放| 色视频在线一区二区三区| 性色av一级| 亚洲国产欧美一区二区综合| 亚洲欧美清纯卡通| 亚洲第一av免费看| 国产精品av久久久久免费| 天美传媒精品一区二区| 在线观看人妻少妇| 只有这里有精品99| 国产精品熟女久久久久浪| 亚洲成色77777| 久久精品久久精品一区二区三区| 久久久精品免费免费高清| 亚洲伊人久久精品综合| 国产成人欧美| 久久久久久久久久久久大奶| 欧美日韩国产mv在线观看视频| 亚洲成人国产一区在线观看 | 日本av免费视频播放| 国产精品久久久久久精品电影小说| 1024香蕉在线观看| 性少妇av在线| 久久久久久免费高清国产稀缺| 国产av码专区亚洲av| 久久久久久久精品精品| 国产淫语在线视频| 国产欧美日韩综合在线一区二区| 女性被躁到高潮视频| 19禁男女啪啪无遮挡网站| 国产片内射在线| 在线观看人妻少妇| 哪个播放器可以免费观看大片| 国产老妇伦熟女老妇高清| 成人亚洲精品一区在线观看| 久久影院123| 青春草视频在线免费观看| 午夜老司机福利片| 一区二区三区激情视频| 亚洲欧美精品自产自拍| 黄色毛片三级朝国网站| 1024香蕉在线观看| 久久ye,这里只有精品| 欧美黄色片欧美黄色片| 久久精品久久精品一区二区三区| 国产高清不卡午夜福利| 国产精品嫩草影院av在线观看| 男女下面插进去视频免费观看| 亚洲欧美中文字幕日韩二区| 亚洲人成电影观看| 午夜精品国产一区二区电影| 国产97色在线日韩免费| 黄色 视频免费看| 色精品久久人妻99蜜桃| 男女边摸边吃奶| 国产人伦9x9x在线观看| 日本91视频免费播放| 日韩一本色道免费dvd| 久久 成人 亚洲| 少妇被粗大的猛进出69影院| 国产精品人妻久久久影院| 丝瓜视频免费看黄片| 亚洲天堂av无毛| 麻豆乱淫一区二区| 高清黄色对白视频在线免费看| 大片免费播放器 马上看| 亚洲成av片中文字幕在线观看| 国产激情久久老熟女| 国产精品久久久av美女十八| 少妇人妻 视频| 女人被躁到高潮嗷嗷叫费观| 免费高清在线观看视频在线观看| 美女国产高潮福利片在线看| 波野结衣二区三区在线| 啦啦啦中文免费视频观看日本| 亚洲欧美成人精品一区二区| 国产片内射在线| 午夜福利乱码中文字幕| 91精品伊人久久大香线蕉| 中文字幕人妻丝袜一区二区 | 高清视频免费观看一区二区| 久久 成人 亚洲| 免费观看人在逋| 一边摸一边抽搐一进一出视频| 久久久久久久久久久久大奶| 国产一级毛片在线| 女人精品久久久久毛片| h视频一区二区三区| 精品第一国产精品| 亚洲成色77777| 精品国产国语对白av| 成人毛片60女人毛片免费| 麻豆精品久久久久久蜜桃| 国产欧美日韩综合在线一区二区| 欧美在线黄色| 9色porny在线观看| 夫妻性生交免费视频一级片| 亚洲国产精品成人久久小说| 日本午夜av视频| 在线观看人妻少妇| 中文字幕最新亚洲高清| 90打野战视频偷拍视频| 老司机在亚洲福利影院| 成人手机av| 日日啪夜夜爽| 国产男人的电影天堂91| 亚洲,欧美精品.| 国产成人a∨麻豆精品| 满18在线观看网站| 妹子高潮喷水视频| e午夜精品久久久久久久| 青春草视频在线免费观看| 久久久久久久久免费视频了| 成人亚洲欧美一区二区av| 久久久久精品性色| 99精品久久久久人妻精品| 少妇精品久久久久久久| 国产一区二区 视频在线| 黑丝袜美女国产一区| 国产黄色视频一区二区在线观看| 亚洲一卡2卡3卡4卡5卡精品中文| 天天躁狠狠躁夜夜躁狠狠躁| 成人18禁高潮啪啪吃奶动态图| 欧美最新免费一区二区三区| 一二三四在线观看免费中文在| 亚洲一卡2卡3卡4卡5卡精品中文| 国产xxxxx性猛交| 精品一区二区三区av网在线观看 | 啦啦啦视频在线资源免费观看| 制服丝袜香蕉在线| 国产一级毛片在线| 777米奇影视久久| 高清视频免费观看一区二区| 亚洲欧美清纯卡通| 国产精品成人在线| 晚上一个人看的免费电影| av福利片在线| 成年av动漫网址| 99国产综合亚洲精品| 色婷婷av一区二区三区视频| 欧美日韩亚洲高清精品| 香蕉国产在线看| 亚洲欧美激情在线| 麻豆av在线久日| a 毛片基地| 成人毛片60女人毛片免费| www日本在线高清视频| 男女之事视频高清在线观看 | 操美女的视频在线观看| 天天躁狠狠躁夜夜躁狠狠躁| 在线观看三级黄色| avwww免费| 国产精品三级大全| 国产不卡av网站在线观看| 亚洲情色 制服丝袜| 久久久久精品人妻al黑| 久久99热这里只频精品6学生| 看免费成人av毛片| 久久国产亚洲av麻豆专区| 国产成人精品在线电影| 欧美日韩国产mv在线观看视频| 在线天堂最新版资源| 婷婷色综合www| 在线看a的网站| 水蜜桃什么品种好| 十八禁人妻一区二区| 久久精品久久久久久久性| 香蕉国产在线看| 久久国产精品男人的天堂亚洲| 久久久久久久精品精品| 永久免费av网站大全| 三上悠亚av全集在线观看| 欧美中文综合在线视频| 五月开心婷婷网| 国产精品免费视频内射| 亚洲国产av新网站| 午夜福利一区二区在线看| 黄色毛片三级朝国网站| 一级,二级,三级黄色视频| 精品亚洲乱码少妇综合久久| 国产一区二区 视频在线| 免费女性裸体啪啪无遮挡网站| 亚洲精品国产区一区二| 亚洲av福利一区| 欧美激情极品国产一区二区三区| 日韩 亚洲 欧美在线| 亚洲美女视频黄频| 18禁国产床啪视频网站| 高清欧美精品videossex| 午夜日本视频在线| 国产精品久久久久久精品古装| 99香蕉大伊视频| 美女视频免费永久观看网站| 蜜桃在线观看..| 欧美精品av麻豆av| 精品酒店卫生间| 无限看片的www在线观看| 天天躁夜夜躁狠狠久久av| 天堂俺去俺来也www色官网| 一区在线观看完整版| 在线观看人妻少妇| www.熟女人妻精品国产| 99国产精品免费福利视频| 天美传媒精品一区二区| 人人妻人人澡人人爽人人夜夜| 国产在视频线精品| 亚洲av欧美aⅴ国产| 最近的中文字幕免费完整| 日本色播在线视频| 丝袜喷水一区| 国产激情久久老熟女| 99re6热这里在线精品视频| 女人爽到高潮嗷嗷叫在线视频| 日韩人妻精品一区2区三区| 啦啦啦啦在线视频资源| 菩萨蛮人人尽说江南好唐韦庄| 在线观看免费视频网站a站| 精品第一国产精品| 老司机影院毛片| 国产 精品1| 欧美日韩视频精品一区| 乱人伦中国视频| 亚洲av福利一区| 国产毛片在线视频| 欧美日韩国产mv在线观看视频| 久久久久久久久久久久大奶| 国产无遮挡羞羞视频在线观看| 久久久久精品久久久久真实原创| 在线观看一区二区三区激情| 成人漫画全彩无遮挡|