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

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

自己買一臺(tái)服務(wù)器做自己的網(wǎng)站市場(chǎng)seo是什么

自己買一臺(tái)服務(wù)器做自己的網(wǎng)站,市場(chǎng)seo是什么,c web網(wǎng)站開發(fā)實(shí)例,鄭州專業(yè)網(wǎng)站制作的公司哪家好大文件上傳是個(gè)非常普遍的場(chǎng)景,在面試中也會(huì)經(jīng)常被問到,大文件上傳的實(shí)現(xiàn)思路和流程。在日常開發(fā)中,無(wú)論是云存儲(chǔ)、視頻分享平臺(tái)還是企業(yè)級(jí)應(yīng)用,大文件上傳都是用戶與服務(wù)器之間交互的重要環(huán)節(jié)。隨著現(xiàn)代網(wǎng)絡(luò)應(yīng)用的日益復(fù)雜化&…

大文件上傳是個(gè)非常普遍的場(chǎng)景,在面試中也會(huì)經(jīng)常被問到,大文件上傳的實(shí)現(xiàn)思路和流程。在日常開發(fā)中,無(wú)論是云存儲(chǔ)、視頻分享平臺(tái)還是企業(yè)級(jí)應(yīng)用,大文件上傳都是用戶與服務(wù)器之間交互的重要環(huán)節(jié)。隨著現(xiàn)代網(wǎng)絡(luò)應(yīng)用的日益復(fù)雜化,大文件上傳已經(jīng)成為前端開發(fā)中不可或缺的一部分。

然而,在實(shí)現(xiàn)大文件上傳時(shí),我們通常會(huì)面臨以下幾個(gè)挑戰(zhàn):

  1. 上傳超時(shí)一般前端請(qǐng)求都會(huì)限制最大請(qǐng)求時(shí)長(zhǎng),比如axios設(shè)置timeout,或者是 nginx(或其它代理/網(wǎng)關(guān)) 限制了最大請(qǐng)求時(shí)長(zhǎng)。

  2. 服務(wù)器壓力:大文件上傳會(huì)給服務(wù)器帶來(lái)較大的壓力,甚至可能導(dǎo)致服務(wù)器崩潰。

  3. 文件大小超限:一般后端都會(huì)對(duì)上傳文件的大小做限制,比如nginx和server都會(huì)限制。

  4. 用戶體驗(yàn):上傳過程中用戶需要等待較長(zhǎng)時(shí)間,用戶體驗(yàn)差。

  5. 網(wǎng)絡(luò)波動(dòng)各種網(wǎng)絡(luò)原因?qū)е律蟼魇?#xff0c;比如網(wǎng)絡(luò)不穩(wěn)定可能導(dǎo)致上傳過程中斷,且失敗之后需要從頭開始。

對(duì)于前三點(diǎn),雖說可以通過一定的配置來(lái)解決,但有時(shí)候也相當(dāng)麻煩,或者服務(wù)器就規(guī)定不允許上傳大型文件,需要兼顧實(shí)際場(chǎng)景。上傳慢的話倒是無(wú)傷大雅,忍一忍是可以接受的,只是體驗(yàn)不好,但是失敗后在重頭開始上傳,在網(wǎng)絡(luò)環(huán)境差的時(shí)候簡(jiǎn)直就是災(zāi)難。為了應(yīng)對(duì)以上挑戰(zhàn),我們就需要用到切片上傳、斷點(diǎn)續(xù)傳等技術(shù)手段。

二、實(shí)現(xiàn)思路分析

整體流程圖如下:

思路如下:

  1. 每個(gè)文件要有自己唯一的標(biāo)識(shí),因此在進(jìn)行分片上傳前,需要對(duì)整個(gè)文件進(jìn)行MD5加密,生成MD5碼,在后面上傳文件每次調(diào)用接口時(shí)以formData格式上傳給后端。可以使用spark-md5 計(jì)算文件的內(nèi)容hash,以此來(lái)確定文件的唯一性將文件hash發(fā)送到服務(wù)端進(jìn)行查詢。以此來(lái)確定該文件在服務(wù)端的存儲(chǔ)情況,這里可以分為三種:未上傳、已上傳、上傳部分。

  2. 根據(jù)服務(wù)端返回的狀態(tài)執(zhí)行不同的上傳策略。已上傳:執(zhí)行秒傳策略,即快速上傳,實(shí)際上沒有對(duì)該文件進(jìn)行上傳,因?yàn)榉?wù)端已經(jīng)有這份文件了。未上傳、上傳部分:執(zhí)行計(jì)算待上傳分塊的策略并發(fā)上傳還未上傳的文件分塊。當(dāng)傳完最后一個(gè)文件分塊時(shí),向服務(wù)端發(fā)送合并的指令,即完成整個(gè)大文件的分塊合并,實(shí)現(xiàn)在服務(wù)端的存儲(chǔ)。

上傳過程:

  1. 分割文件:將要上傳的文件切割成多個(gè)小文件片段。主要使用JavaScript的File API中的slice方法來(lái)實(shí)現(xiàn)。

  2. 上傳文件分片:使用XMLHttpRequest或者Fetch API將分片信息以formData格式,并攜帶相關(guān)信息,如文件名、文件ID、當(dāng)前片段序號(hào)等參數(shù)傳給分片接口。

  3. 后端接收并保存文件片段:后端接收到每個(gè)文件片段后,將其保存在臨時(shí)位置,并記錄文件片段的序號(hào)、文件ID和文件MD5 hash值等信息。

  4. 續(xù)傳處理:如果上傳過程中斷,下次繼續(xù)上傳時(shí),通過查詢后端已保存的文件片段信息,得知需要上傳的文件片段,從斷點(diǎn)處繼續(xù)上傳剩余的文件片段。

  5. 合并文件:當(dāng)所有文件片段都上傳完成后,后端根據(jù)文件ID將所有片段合并成完整的文件。

三、切片上傳

切片上傳原理:通過使用JavaScript的File API中的slice方法將大文件分割成多個(gè)小片段(chunk),然后逐個(gè)上傳每個(gè)片段,在上傳完切片后,前端通知后臺(tái)再將文件片段拼接為一個(gè)完整的文件。

這樣做的優(yōu)點(diǎn)是可以并行多個(gè)請(qǐng)求一起上傳文件,提高上傳效率,并且在上傳過程中如果某個(gè)片段因?yàn)槟承┰蛏蟼魇?#xff0c;也不會(huì)影響其它文件切片,只需要重新上傳該失敗片段即可,不必重新上傳整個(gè)文件。

實(shí)現(xiàn)思路:

在JavaScript中,文件File對(duì)象是Blob對(duì)象的子類,Blob對(duì)象包含了slice方法,通過這個(gè)方法,可以對(duì)二進(jìn)制文件進(jìn)行拆分。循環(huán)發(fā)送多個(gè)上傳請(qǐng)求,然后返回結(jié)果后計(jì)數(shù),當(dāng)計(jì)數(shù)達(dá)到file片段長(zhǎng)度后終止上傳。

<input type="file" name="file" id="file" />
const eleFile = document.getElementById('file');
eleFile.addEventListener('change', (event) => {const file = event.target.files[0];// 上傳分塊大小,單位Mbconst chunkSize = 1024 * 1024 * 1;// 當(dāng)前已執(zhí)行分片數(shù)位置let currentPosition = 0;//初始化分片方法,兼容問題let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;while(currentPosition < file.size) {const chunk = blobSlice.call(file, currentPosition, currentPosition + chunkSize);uploadChunk(chunk);currentPosition += chunkSize;}
})function uploadChunk(chunk) {// 將分片信息以formData格式作為參數(shù)傳給分片接口let formData = new FormData();formData.append('fileChunk', chunk);// 根據(jù)項(xiàng)目實(shí)際情況axios.post('/api/oss/upload/file', formData, {headers: { 'Content-Type': 'multipart/form-data' },timeout: 600000,}).then(res => {// 上傳成功console.log('分片上傳成功', res)}).catch(error => {// 上傳失敗console.log('分片上傳失敗', error)})
}

四、并發(fā)上傳

并發(fā)上傳相對(duì)要優(yōu)雅一下,將文件分割成小片段后,使用Promise.all()把所有請(qǐng)求都放到一個(gè)Promise.all里,它會(huì)自動(dòng)判斷所有請(qǐng)求都完成然后觸發(fā) resolve 方法。并發(fā)上傳可以同時(shí)上傳多個(gè)片段而不是依次上傳,進(jìn)一步提高效率。

實(shí)現(xiàn)思路:

1、使用slice方法對(duì)二進(jìn)制文件進(jìn)行拆分,并把拆分的片段放到chunkList里面。

2、使用map將chunkList里面的每個(gè)chunk映射到一個(gè)Promise上傳方法。

3、把所有請(qǐng)求都放到一個(gè)Promise.all里,它會(huì)自動(dòng)判斷所有請(qǐng)求都完成然后觸發(fā) resolve 方法,上傳成功后通知后端合并分片文件。

代碼實(shí)現(xiàn)如下:

const eleFile = document.getElementById('file');
eleFile.addEventListener('change', (event) => {const file = event.target.files[0];// 上傳分塊大小,單位Mbconst chunkSize = 1024 * 1024 * 1;// 當(dāng)前已執(zhí)行分片數(shù)位置let currentPosition = 0;// 存儲(chǔ)文件的分片let chunkList = [];//初始化分片方法,兼容問題let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;while(currentPosition < file.size) {const chunk = blobSlice.call(file, currentPosition, currentPosition + chunkSize);chunkList.push(chunk);currentPosition += chunkSize;}uploadChunk(chunkList, file.name)
})function uploadChunk(chunkList, fileName) {const uploadPromiseList = chunkList.map((chunk, index) => {// 將分片信息以formData格式作為參數(shù)傳給分片接口let formData = new FormData();formData.append('fileChunk', chunk);// 可以根據(jù)實(shí)際的需要添加其它參數(shù),比如切片的索引formData.append('index', index);// 根據(jù)項(xiàng)目實(shí)際情況return axios.post('/api/oss/upload/file', formData, {headers: { 'Content-Type': 'multipart/form-data' },timeout: 600000,})})Promise.all(uploadPromiseList).then(res => {// 上傳成功并通知后端合并分片文件axios.post('/api/oss/file/merge', {message: fileName},{headers: { 'Content-Type': 'application/json' },timeout: 600000,}).then(data => {console.log('文件合并成功', data)})}).catch(error => {// 上傳錯(cuò)誤console.log('上傳失敗', error)})
}

五、斷點(diǎn)續(xù)傳之1

斷點(diǎn)續(xù)傳允許在網(wǎng)絡(luò)中斷或其它原因?qū)е律蟼魇r(shí),從上次上傳中斷的位置繼續(xù)上傳,而不是重新從頭上傳整個(gè)文件。

實(shí)現(xiàn)斷點(diǎn)續(xù)傳需要后端配合記錄上傳的進(jìn)度,并且在前端重新上傳時(shí),需要先查詢已上傳的進(jìn)度,讓后從斷點(diǎn)處繼續(xù)上傳。

const eleFile = document.getElementById('file');
eleFile.addEventListener('change', (event) => {const file = event.target.files[0];// 上傳分塊大小,單位Mbconst chunkSize = 1024 * 1024 * 1;// 當(dāng)前已執(zhí)行分片數(shù)位置let currentPosition = 0;// 存儲(chǔ)文件的分片let chunkList = [];//初始化分片方法,兼容問題let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;while(currentPosition < file.size) {const chunk = blobSlice.call(file, currentPosition, currentPosition + chunkSize);chunkList.push(chunk);currentPosition += chunkSize;}axios.post('/api/upload/file/history',{fileName: file.name},{headers: { 'Content-Type': 'multipart/form-data' },timeout: 600000,}).then(res => {const historyChunks = res.uploadedChunks;const remainChunks = chunkList.filter((item, index) => !historyChunks.includes(index));// 并發(fā)上傳剩余分片uploadChunk(remainChunks, file.name)})
})function uploadChunk(chunkList, fileName) {const uploadPromiseList = chunkList.map((chunk, index) => {// 將分片信息以formData格式作為參數(shù)傳給分片接口let formData = new FormData();formData.append('fileChunk', chunk);// 可以根據(jù)實(shí)際的需要添加其它參數(shù),比如切片的索引formData.append('index', index);// 根據(jù)項(xiàng)目實(shí)際情況return axios.post('/api/oss/upload/file', formData, {headers: { 'Content-Type': 'multipart/form-data' },timeout: 600000,})})Promise.all(uploadPromiseList).then(res => {// 剩余分片上傳成功并通知后端合并分片文件axios.post('/api/oss/file/merge', {message: fileName},{headers: { 'Content-Type': 'application/json' },timeout: 600000,}).then(data => {console.log('文件合并成功', data)})}).catch(error => {// 上傳錯(cuò)誤console.log('上傳失敗', error)})
}

以上是一個(gè)簡(jiǎn)易版的斷點(diǎn)續(xù)傳實(shí)現(xiàn)流程代碼,但在實(shí)際場(chǎng)景應(yīng)用中我們還需要更嚴(yán)謹(jǐn)?shù)奶幚韥?lái)實(shí)現(xiàn)斷點(diǎn)續(xù)傳功能。不如,上傳文件前通常需要生成文件的唯一標(biāo)識(shí),比如文件名與文件大小的組合、文件的hash值或者文件hash值與文件大小的組合來(lái)支持?jǐn)帱c(diǎn)續(xù)傳的邏輯。請(qǐng)繼續(xù)看下面的代碼實(shí)現(xiàn)!!!

六、斷點(diǎn)續(xù)傳之2

已上傳的執(zhí)行秒傳策略,即快速上傳,實(shí)際上沒有對(duì)該文件進(jìn)行上傳,因?yàn)榉?wù)端已經(jīng)有這份文件了。

秒傳的關(guān)鍵在于計(jì)算文件的唯一性標(biāo)識(shí)。文件的不同不是命名的差異,而是內(nèi)容的差異,所以我們將整個(gè)文件的二進(jìn)制碼作為入?yún)?#xff0c;計(jì)算 Hash 值,將其作為文件的唯一性標(biāo)識(shí)。一般而言,這樣做就夠了,但是摘要算法是存在碰撞概率的,我們?nèi)绻胍賴?yán)謹(jǐn)點(diǎn)的話,可以將文件大小也作為衡量指標(biāo),只有文件摘要和文件大小同時(shí)相等,才認(rèn)為是相同的文件。

<input type="file" name="file" id="file" @change="changeFile" />

計(jì)算文件hash值可以使用spark-md5。

import SparkMD5 from 'spark-md5'

通過input的change事件獲取要上傳的文件。

function changeFile(event) {const file = event.target.files[0];handleUploadFile(file, 1)
}

接下來(lái)對(duì)文件進(jìn)行分片和hash計(jì)算:

/*** @param {File} file 目標(biāo)上傳文件* @param {number} size 上傳分塊大小,單位Mb* @returns {filelist:ArrayBuffer,fileHash:string}*/
async function handleSliceFile(file, size = 1) {return new Promise((resolve, reject) => {// 上傳分塊大小,單位Mbconst chunkSize = 1024 * 1024 * size;// 分片數(shù)const totalChunkCount = file && Math.ceil(file.size / chunkSize);// 當(dāng)前已執(zhí)行分片數(shù)位置let currentChunkCount = 0;// 存儲(chǔ)文件的分片let fileList = [];//初始化分片方法,兼容問題let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;// 文件讀取對(duì)象const fileReader = new FileReader();// spark-md5 計(jì)算文件hash值SparkMD5對(duì)象const spark = new SparkMD5.ArrayBuffer();// 存儲(chǔ)計(jì)算后的文件hash值let fileHash = "";// 錯(cuò)誤fileReader.onerror = function () {reject('Error reading file');};fileReader.onload = (e) => {//當(dāng)前讀取的分塊結(jié)果 ArrayBufferconst curChunk = e.target.result;//將當(dāng)前分塊追加到spark對(duì)象中spark.append(curChunk);currentChunkCount++;fileList.push(curChunk);//判斷分塊是否完成if (currentChunkCount >= totalChunkCount) {// 全部讀取,獲取文件hashfileHash = spark.end();resolve({ fileList, fileHash });} else {readNext();}};//讀取下一個(gè)分塊const readNext = () => {//計(jì)算分片的起始位置和終止位置const start = chunkSize * currentChunkCount;let end = start + chunkSize;if (end > file.size) {end = file.size}//讀取文件,觸發(fā)onLoadfileReader.readAsArrayBuffer(blobSlice.call(file, start, end))}readNext()})
}

文件上傳,首選調(diào)用接口獲取需要上傳的文件index,返回的集合length等于0執(zhí)行秒傳,如果返回的集合length不等于0執(zhí)行需要過濾得到需要上傳的remainingChunks,使用map將remainingChunks里面的每個(gè)chunk映射為一個(gè)Promise上傳方法,把所有請(qǐng)求都放到一個(gè)Promise.all里,上傳成功后通知后端合并分片文件。

sync function handleUploadFile(file, chunkSize) {const { fileList, fileHash } = await handleSliceFile(file, chunkSize);// 存放切片let chunkList = fileList;// 顯示上傳的進(jìn)度條let process = 0;// 獲取文件上傳狀態(tài)const { data } = await axios.post('/api/upload/file/history', {fileHash,totalCount: chunkList.length,extname: file.name,})// 返回已經(jīng)上傳的const { needUploadChunks } = data;// 已上傳,無(wú)待上傳文件,秒傳if (!needUploadChunks.length) {process = 100;return;} // 此處包含了未上傳和上傳部分的情況// 過濾剩余需要上傳的分片序列const remainingChunks = chunkList.filter((item, index) => needUploadChunks.includes(index + 1));// 同步上傳進(jìn)度,斷點(diǎn)續(xù)傳情況下progress = ((chunkList.length - needUploadChunks.length) / chunkList.length) * 100;// 上傳if (remainingChunks.length) {const uploadPromiseList = remainingChunks.map(async (chunk, index) => {const response = await uploadChunk(chunk, index + 1, fileHash);//更新進(jìn)度progress += Math.ceil(100 / allChunkList.length);if (progress >= 100) progress = 100;return response;});Promise.all(uploadPromiseList).then(() => {// 清空已上傳的切片chunkList = [];//發(fā)送請(qǐng)求,通知后端進(jìn)行合并axios.post('/api/file/merge', {fileHash,extname: 'fileName.mp4'}, {headers: { 'Content-Type': 'multipart/form-data' },timeout: 600000,}).then(res => {console.log('合并完成', res)}).catch(error => {// 合并錯(cuò)誤console.log('合并錯(cuò)誤', error)})}).catch(error => {// 上傳錯(cuò)誤console.log('上傳錯(cuò)誤', error)})}
}

上傳函數(shù)返回一個(gè)promise,參數(shù)為formData。

function uploadChunk(chunk, index, fileHash) {// 將分片信息以formData格式作為參數(shù)傳給分片接口let formData = new FormData();formData.append('fileChunk', new Blob([chunk]));// 可以根據(jù)實(shí)際的需要添加其它參數(shù),比如切片的索引formData.append('index', index);// 文件的標(biāo)識(shí)hash值formData.append('fileHash', fileHash);// 根據(jù)項(xiàng)目實(shí)際情況return axios.post('/api/upload/file', formData, {headers: { 'Content-Type': 'multipart/form-data' },timeout: 600000,})
}

我們?cè)?fileReader 里面使用了?readAsArrayBuffer 方法做轉(zhuǎn)換并分割,因此傳入的chunk的類型是ArrayBuffer,而formData中文件的類型應(yīng)該是Blob,所以需要時(shí)用new Blob() 將每一個(gè)chunk轉(zhuǎn)為Blob類型。

七、總結(jié)

斷點(diǎn)續(xù)傳的重點(diǎn)是文件的切片與合并,整個(gè)上傳流程需要前后端配合好,細(xì)節(jié)較多。

注意事項(xiàng):

  1. 計(jì)算整個(gè)文件的 MD5 值,當(dāng)大文件比較大時(shí)會(huì)比較慢,耗時(shí),更好地做法是將這部分任務(wù)放在 Web Worker 中執(zhí)行。Web Worker 是 HTML5 標(biāo)準(zhǔn)的一部分,它允許一段 JavaScript 程序運(yùn)行在主線程之外的另外一個(gè)線程中。這樣計(jì)算任務(wù)就不會(huì)影響到當(dāng)前線程的渲染任務(wù)??梢院彤?dāng)前線程間使用 postMessage 的方式進(jìn)行通訊。

  2. 可以根據(jù)文件切片的狀態(tài),發(fā)送上傳請(qǐng)求,由于存在并發(fā)限制,需要限制 request 創(chuàng)建個(gè)數(shù),避免頁(yè)面卡死。

  3. 在上傳大文件時(shí),應(yīng)提供適當(dāng)?shù)倪M(jìn)度反饋和錯(cuò)誤處理以確保良好的用戶體驗(yàn)。

  4. 對(duì)于文件切片、并發(fā)上傳和斷點(diǎn)續(xù)傳,后端需要能夠接受文件片段,并能夠處理并發(fā)請(qǐng)求和斷點(diǎn)數(shù)據(jù),因此需要合后端人員密切配合。

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

相關(guān)文章:

  • 建設(shè)項(xiàng)目社會(huì)招標(biāo)上那個(gè)網(wǎng)站網(wǎng)絡(luò)推廣哪個(gè)平臺(tái)效果最好
  • 平安建設(shè)宣傳音頻免費(fèi)下載網(wǎng)站微網(wǎng)站建站平臺(tái)
  • 網(wǎng)站建設(shè)后期服務(wù)收費(fèi)標(biāo)準(zhǔn)口碑營(yíng)銷的成功案例
  • 網(wǎng)站建設(shè)業(yè)務(wù)員論壇漳州seo網(wǎng)站快速排名
  • 可以做pos機(jī)的網(wǎng)站關(guān)鍵詞排名怎么查
  • tripod wordpressseo免費(fèi)課程視頻
  • 如何做自助網(wǎng)站百度如何收錄網(wǎng)站
  • 沈陽(yáng)建設(shè)工程信息網(wǎng) 最佳中項(xiàng)網(wǎng)公眾號(hào)seo排名優(yōu)化
  • 網(wǎng)絡(luò)服務(wù)器設(shè)備福清市百度seo
  • 國(guó)家企業(yè)信用信息公示網(wǎng)官網(wǎng)查詢seo平臺(tái)有哪些
  • 網(wǎng)站建設(shè)饣金手指科杰十二西安百度
  • 十大黃臺(tái)軟件app下載如何提高搜索引擎優(yōu)化
  • css怎么做響應(yīng)式網(wǎng)站排名優(yōu)化外包公司
  • 凡科建站的怎么取消手機(jī)網(wǎng)站怎樣建立自己的網(wǎng)站平臺(tái)
  • 哪些網(wǎng)站可以做ppt賺錢域名注冊(cè)網(wǎng)
  • stanley工具網(wǎng)站開發(fā)品牌策略
  • 室內(nèi)設(shè)計(jì)學(xué)校排行榜合肥正規(guī)的seo公司
  • 英文版網(wǎng)站案例廣州營(yíng)銷課程培訓(xùn)班
  • 根據(jù)網(wǎng)站日志做seoseo就業(yè)前景
  • 醫(yī)院網(wǎng)站建設(shè)情況百度競(jìng)價(jià)是什么意思?
  • 免費(fèi)營(yíng)銷網(wǎng)站制作模板搜索引擎的網(wǎng)址有哪些
  • 溫江建網(wǎng)站免費(fèi)推廣網(wǎng)站有哪些
  • 三五互聯(lián)做網(wǎng)站嗎如何進(jìn)行品牌宣傳與推廣
  • 國(guó)外免費(fèi)建站腦白金網(wǎng)絡(luò)營(yíng)銷
  • 武漢品牌網(wǎng)站設(shè)計(jì)海外發(fā)布新聞
  • 池州網(wǎng)站建設(shè)開發(fā)合肥網(wǎng)絡(luò)推廣優(yōu)化公司
  • 云南網(wǎng)站制作公司搜索引擎優(yōu)化需要多少錢
  • 做標(biāo)識(shí)的網(wǎng)站 知乎百度搜索推廣方法
  • 網(wǎng)站seo診斷工具如何做好線上推廣
  • 雨默合肥做網(wǎng)站推廣永久觀看不收費(fèi)的直播