摘要:背景當(dāng)知道要上傳的視頻資料從條變成條時(shí),我就明白,絕對(duì)不能再人工處理了。
背景
當(dāng)知道要上傳的視頻資料從20條變成100條時(shí),我就明白,絕對(duì)不能再人工處理了。他們總是想當(dāng)然的認(rèn)為,錄入一條數(shù)據(jù)需要1分鐘,那錄入20條數(shù)據(jù)就是20分鐘,錄入100條數(shù)據(jù),不就是100分鐘嗎?我有時(shí)候,真的很想問(wèn)問(wèn)他們,沒(méi)有考慮過(guò)人是會(huì)犯錯(cuò)的嗎?數(shù)據(jù)越多,出錯(cuò)的可能就越大;但是數(shù)據(jù)本身,又是不允許出現(xiàn)紕漏的。那拿什么去保證數(shù)據(jù)的正確性?刷臉?可能嗎?
大多數(shù)時(shí)候,類(lèi)似的爭(zhēng)論,最終幾乎總是會(huì)以他們的一句“我不懂技術(shù),你們看著辦吧”結(jié)束。所以,也懶得去做口舌之爭(zhēng)。我盡力盡快做;但是你承不承認(rèn)事情本身的復(fù)雜度,并不會(huì)影響事情本身的復(fù)雜度。
回到問(wèn)題本身,究竟如何處理新到來(lái)的100條數(shù)據(jù)以及以后更多的數(shù)據(jù),確實(shí)是一個(gè)必須想辦法徹底解決下的問(wèn)題。
我拿到的原始數(shù)據(jù)此處適當(dāng)象征性的描述下我拿到的數(shù)據(jù)。以下討論,單以 10 條數(shù)據(jù)為例。
一個(gè) word 文檔,是一組問(wèn)題。內(nèi)容假定是:
1.【smart-transform】取自 Atom 的 babeljs&coffeescript&typescript 智能轉(zhuǎn) es5 庫(kù) 2.【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi) 3.【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器 4.【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制 5.【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘 6. 借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派 7.【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境 8.【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境 9. 關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考 10.記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug
是的,內(nèi)容中還有各種中文標(biāo)點(diǎn)。他們有相當(dāng)一部分人不理解攻城獅為什么喜歡用英文標(biāo)點(diǎn),甚至還有人以此為由說(shuō)我們小學(xué)標(biāo)點(diǎn)符號(hào)沒(méi)學(xué)好。懶得解釋那么多,但是既然給出來(lái)了,作為純文本,也不用管這么多,照單全收就行了。符號(hào)習(xí)慣問(wèn)題本身,也是一個(gè)無(wú)傷大雅的問(wèn)題。
另一個(gè) word 文檔,是一組問(wèn)題對(duì)應(yīng)的 Luis 語(yǔ)義分析結(jié)果微軟的 Luis 語(yǔ)義分析服務(wù),勉強(qiáng)算是和人工智能沾點(diǎn)邊吧,感興趣的請(qǐng)自行了解下。從客戶端角度來(lái)說(shuō),你給它一個(gè)文本字符串,他們分析出來(lái)和這個(gè)字符串匹配度最高的某個(gè)預(yù)錄入的答案的唯一標(biāo)記。每個(gè)唯一標(biāo)記 ID,被稱作一個(gè) intent。每次請(qǐng)求,最多只有一個(gè)匹配度最高的 intent。
感覺(jué)已經(jīng)有的 word 問(wèn)題,我們的后端小伙伴,送來(lái)了另一個(gè) word 文檔:
1. smart_transform 2. memory_leakDetector 3. sinopia_npm 4. frp_ip 5. tip_rest 6. frp_anywhere 7. luajit_macos 8. lua_macos 9. app_future 10. google_bug
又是非結(jié)構(gòu)化的數(shù)據(jù)。顯而易見(jiàn),我們可愛(ài)的后端同學(xué),只是簡(jiǎn)單完成了錄入,自己沒(méi)有做必要的單元測(cè)試。這是在等著我去發(fā)現(xiàn)問(wèn)題啊。很久很久以前,我總是幻想著,所有的攻城獅,必然都是各種自動(dòng)化測(cè)試用例,就像樹(shù)上寫(xiě)的各種敏捷,各種快速迭代。事實(shí)上,我見(jiàn)到的許多所謂的敏捷式開(kāi)發(fā),最終其實(shí)只是把成本后置,各種技術(shù)債。出來(lái)混,真的遲早是要換的。100個(gè)問(wèn)題,逐一去驗(yàn)證,真的是很耗費(fèi)時(shí)間的,而且最終有問(wèn)題的,數(shù)量也不會(huì)太多。也就說(shuō)說(shuō),如果手動(dòng)去做,很有可能尋找問(wèn)題的時(shí)間,要遠(yuǎn)遠(yuǎn)大于發(fā)現(xiàn)問(wèn)題的時(shí)間。所以,自動(dòng)化批量測(cè)試,是顯而易見(jiàn)的。根據(jù)不同的場(chǎng)景和需要,快速構(gòu)建基本夠用的批量自動(dòng)化測(cè)試工具鏈,應(yīng)該成為每個(gè)攻城獅的必修課。
一組勉強(qiáng)算是有規(guī)律的分文件夾放置的視頻我依然是象征性的描述下,結(jié)構(gòu)類(lèi)似于:
/videos/樹(shù)莓派/【smart-transform】取自 Atom 的 babeljs&coffeescript&typescript 智能轉(zhuǎn) es5 庫(kù).mp4 /videos/樹(shù)莓派/【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi).mp4 /videos/樹(shù)莓派/【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器.mp4 /videos/樹(shù)莓派/【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制.mp4 /videos/frp/【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘.mp4 /videos/frp/借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派.mp4 /videos/Lua/【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境.mp4 /videos/Lua/【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境.mp4 /videos/Lua/關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考.mp4 /videos/Lua/記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug.mp4目標(biāo)數(shù)據(jù)要求 intent 必須和問(wèn)題關(guān)聯(lián)起來(lái)
顯而易見(jiàn),應(yīng)該使用 intent 作為數(shù)據(jù)的唯一 id。為了便于處理,索性寫(xiě)成了一個(gè) JS 模塊。之所以不直接用 JSON,是因?yàn)槟K比 JSON 文件,更靈活性,后期擴(kuò)展方便,如果有的話。
這一步是必須手動(dòng)做的,或者說(shuō)總是需要有一個(gè)人手動(dòng)去做的。為了效率,團(tuán)隊(duì)內(nèi)總是需要有一個(gè)人必須要充當(dāng)這個(gè)角色。
大致處理下,初版結(jié)構(gòu) intent_info.js 大概類(lèi)似這樣:
module.exports = { /* 樹(shù)莓派 */ "smart_transform":"【smart-transform】取自 Atom 的 babeljs&coffeescript&typescript 智能轉(zhuǎn) es5 庫(kù)", "memory_leakDetector":"【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi)", "sinopia_npm":"【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器", "frp_ip":"【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制", /* frp */ "tip_rest":"【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘", "frp_anywhere":"借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派", /* Lua */ "luajit_macos":"【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "lua_macos":"【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "app_future":"關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考", "google_bug":"記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug", }排序
排序,是需要增加一個(gè)新的字段 order。不過(guò),我就直接上面的類(lèi)似 JSON 的結(jié)構(gòu)來(lái)排序的。因?yàn)榕判蚴怯闪硗庖粋€(gè)人做,懂技術(shù),操作很簡(jiǎn)單些。
經(jīng)過(guò)對(duì)方排序后,intent_info.js,可能變成了這樣:
module.exports = { /* 樹(shù)莓派 */ "smart_transform":"【smart-transform】取自 Atom 的 babeljs&coffeescript&typescript 智能轉(zhuǎn) es5 庫(kù)", "memory_leakDetector":"【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi)", "sinopia_npm":"【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器", "frp_ip":"【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制", /* Lua */ "luajit_macos":"【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "lua_macos":"【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "app_future":"關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考", "google_bug":"記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug", /* frp */ "tip_rest":"【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘", "frp_anywhere":"借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派", }
在上面的優(yōu)先顯示。在真正生成 order 字段時(shí),是借助 Node 一個(gè)不太可靠的特性: 字典遍歷時(shí),會(huì)基于key的書(shū)寫(xiě)順序來(lái)遍歷。這一點(diǎn),在 Node 和 Android 瀏覽器上都是成立的,在 safari 上,無(wú)效。一般開(kāi)發(fā)時(shí),不應(yīng)依賴于這一點(diǎn),不過(guò)目前,我只是需要一個(gè)夠用的東西。Node 的這個(gè)特性,在短時(shí)間內(nèi),應(yīng)該是不會(huì)有改變的。
分類(lèi)沒(méi)過(guò)幾天,果然又加了新需求,說(shuō)是視頻太多了,太雜亂,想給每個(gè)視頻加個(gè)分類(lèi),然后可以按分類(lèi)查看視頻。
好,那我給你加個(gè)分類(lèi):
module.exports = { /* 樹(shù)莓派 */ "樹(shù)莓派":"_category", "smart_transform":"【smart-transform】取自 Atom 的 babeljs&coffeescript&typescript 智能轉(zhuǎn) es5 庫(kù)", "memory_leakDetector":"【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi)", "sinopia_npm":"【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器", "frp_ip":"【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制", /* Lua */ "Lua":"_category", "luajit_macos":"【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "lua_macos":"【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "app_future":"關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考", "google_bug":"記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug", /* frp */ "frp":"_category", "tip_rest":"【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘", "frp_anywhere":"借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派", }
新加了幾個(gè)值為 _category 的字段。當(dāng)檢測(cè)到值為 _category 時(shí),就自動(dòng)判定為是一個(gè)分類(lèi)。我這種處理方式,免不了引來(lái)一陣唏噓。但是,許多時(shí)候,你選擇的技術(shù)策略,都必須根據(jù)項(xiàng)目所處的狀態(tài)和各種條件,去綜合權(quán)衡。我只有幾十分鐘時(shí)間去重新規(guī)劃和整理100條數(shù)據(jù)。可能真的沒(méi)法想太多。需求總是變化的,不知道明天又會(huì)變成什么樣,可能再進(jìn)一步,就變成”過(guò)度設(shè)計(jì)“了。另外,項(xiàng)目本身, intent 本身約定了自己特有命名規(guī)律,是可以安全認(rèn)為 intent 和 分類(lèi)一定不會(huì)重復(fù)的。
問(wèn)題和視頻關(guān)聯(lián)在讀取 intent_info.js 中的足夠可信的結(jié)構(gòu)化數(shù)據(jù)后,我會(huì)動(dòng)態(tài)建立問(wèn)題和視頻的關(guān)聯(lián)。這個(gè)過(guò)程中,可能需要適當(dāng)修改問(wèn)題和視頻的標(biāo)題。為了避免遺漏,一個(gè)標(biāo)題,如果沒(méi)有對(duì)應(yīng)的視頻或?qū)?yīng)多個(gè)視頻,就直接crash。有些霸道,但總比后期一個(gè)一個(gè)比對(duì)排查,省太多事了。結(jié)合問(wèn)題和視頻標(biāo)題的特點(diǎn),我專(zhuān)門(mén)封裝了一個(gè)方法:
/* 獲取某個(gè)標(biāo)題對(duì)應(yīng)的本地路徑. 為了避免未知錯(cuò)誤,如果找不到或找到多個(gè),就直接 crash. @return 本地視頻的相對(duì)路徑. */ function localVideoPath(title) { let path = require("path") let fs = require ("fs-plus") let fse = require("fs-extra") let os = require("os") let {execSync} = require("child_process") let videoDir = path.resolve(__dirname,"./videos") let videos = fs.listTreeSync(videoDir) .filter(item=>{ return [".mov",".mp4"].includes(path.extname(item)) }) .map(item=>{ return path.relative(__dirname,item) }) /* 一個(gè)標(biāo)題,能且只能對(duì)應(yīng)一個(gè)視頻,否則就拋出異常. */ let localVideoPath = null for (let item of videos) { if (item.includes(title)) { if (localVideoPath) { const tip = `致命異常: ${title} 對(duì)應(yīng)的視頻重復(fù): ${localVideoPath} ${item}` throw new Error(tip) } localVideoPath = item } } if (!localVideoPath) { const tip = `致命異常!這個(gè)標(biāo)題竟然沒(méi)有對(duì)應(yīng)的視頻: ${title}` throw new Error(tip) } return localVideoPath }見(jiàn)碼如唔
完整的自動(dòng)化處理成結(jié)構(gòu)數(shù)據(jù)的邏輯如下,都集中在 make_data.js 中。
/* 生成帶有排序等信息的文件. */ /* 支持自動(dòng)生成數(shù)據(jù). */ makeDataWithOrder() function makeDataWithOrder() { const fs = require("fs-extra") const path = require("path") const intentInfo = require("./intent_info.js") let intentInfoNew = [] let index = 1 /* 在node中遍歷時(shí),key的順序是和原始key的順序?qū)?yīng)的. 這個(gè)特性,并不總是有效,比如在 ios 瀏覽器中. 目前,僅僅是夠用. */ let category = "" for (let intent in intentInfo) { if (intentInfo[intent] == "_category") { /* 說(shuō)明是一個(gè)分類(lèi)標(biāo)記. */ category = intent continue } let title = intentInfo[intent] const local_path = localVideoPath(title) intentInfoNew.push({ "type":"video", "content":"", "intent": intent, "title": title, "order": index, "local_video_path": local_path, "ext": path.extname(local_path), "category":category, }) ++ index } localVideoLoseCheck(intentInfoNew) const dataPath = path.resolve(__dirname, "./data.json") fs.writeJsonSync(dataPath, intentInfoNew) console.log(`恭喜!數(shù)據(jù)已寫(xiě)入 ${dataPath}`) } /* 確保視頻總數(shù)與intent總數(shù)是對(duì)應(yīng)的,防止有視頻遺漏. 有視頻沒(méi)有對(duì)應(yīng)問(wèn)題時(shí),會(huì)直接拋出異常. */ function localVideoLoseCheck(intents) { /* 先把視頻信息處理成 key-value. */ let path = require("path") let fs = require ("fs-plus") let fse = require("fs-extra") let os = require("os") let {execSync} = require("child_process") let videoDir = path.resolve(__dirname,"./videos") let videoDict = fs.listTreeSync(videoDir) .filter(item=>{ return [".mov",".mp4"].includes(path.extname(item)) }) .map(item=>{ return path.relative(__dirname,item) }) .reduce((sum,item,idx)=>{ sum[item] = false return sum },{}) for (let item of intents) { videoDict[item.local_video_path] = true } /* 尋找缺失的. */ let loses = [] for (let item in videoDict) { if (!videoDict[item]) { loses.push(item) } } if (loses.length) { const tip = `一下 ${loses.length} 個(gè)視頻沒(méi)有對(duì)應(yīng)的問(wèn)題: ${JSON.stringify(loses)}` throw new Error(tip) } } /* 獲取某個(gè)標(biāo)題對(duì)應(yīng)的本地路徑. 為了避免未知錯(cuò)誤,如果找不到或找到多個(gè),就直接 crash. @return 本地視頻的相對(duì)路徑. */ function localVideoPath(title) { let path = require("path") let fs = require ("fs-plus") let fse = require("fs-extra") let os = require("os") let {execSync} = require("child_process") let videoDir = path.resolve(__dirname,"./videos") let videos = fs.listTreeSync(videoDir) .filter(item=>{ return [".mov",".mp4"].includes(path.extname(item)) }) .map(item=>{ return path.relative(__dirname,item) }) /* 一個(gè)標(biāo)題,能且只能對(duì)應(yīng)一個(gè)視頻,否則就拋出異常. */ let localVideoPath = null for (let item of videos) { if (item.includes(title)) { if (localVideoPath) { const tip = `致命異常: ${title} 對(duì)應(yīng)的視頻重復(fù): ${localVideoPath} ${item}` throw new Error(tip) } localVideoPath = item } } if (!localVideoPath) { const tip = `致命異常!這個(gè)標(biāo)題竟然沒(méi)有對(duì)應(yīng)的視頻: ${title}` throw new Error(tip) } return localVideoPath }
我們?cè)陧?xiàng)目目錄執(zhí)行
node ./make_data.js
就可以得到我們想要的結(jié)構(gòu)化的數(shù)據(jù):
[ { "type": "video", "content": "", "intent": "smart_transform", "title": "【smart-transform】取自 Atom 的 babeljs:coffeescript:typescript 智能轉(zhuǎn) es5 庫(kù)", "order": 1, "local_video_path": "videos/樹(shù)莓派/【smart-transform】取自 Atom 的 babeljs:coffeescript:typescript 智能轉(zhuǎn) es5 庫(kù).mp4", "ext": ".mp4", "category": "樹(shù)莓派" }, { "type": "video", "content": "", "intent": "memory_leakDetector", "title": "【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi)", "order": 2, "local_video_path": "videos/樹(shù)莓派/【YFMemoryLeakDetector】人人都能理解的 iOS 內(nèi)存泄露檢測(cè)工具類(lèi).mp4", "ext": ".mp4", "category": "樹(shù)莓派" }, { "type": "video", "content": "", "intent": "sinopia_npm", "title": "【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器", "order": 3, "local_video_path": "videos/樹(shù)莓派/【玩轉(zhuǎn)樹(shù)莓派】使用 sinopia 搭建私有 npm 服務(wù)器.mp4", "ext": ".mp4", "category": "樹(shù)莓派" }, { "type": "video", "content": "", "intent": "frp_ip", "title": "【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制", "order": 4, "local_video_path": "videos/樹(shù)莓派/【小技巧解決大問(wèn)題】使用 frp 突破阿里云主機(jī)無(wú)彈性公網(wǎng) IP 不能用作 Web 服務(wù)器的限制.mp4", "ext": ".mp4", "category": "樹(shù)莓派" }, { "type": "video", "content": "", "intent": "luajit_macos", "title": "【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "order": 5, "local_video_path": "videos/Lua/【LuaJIT版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境.mp4", "ext": ".mp4", "category": "Lua" }, { "type": "video", "content": "", "intent": "lua_macos", "title": "【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境", "order": 6, "local_video_path": "videos/Lua/【最新版】從零開(kāi)始在 macOS 上配置 Lua 開(kāi)發(fā)環(huán)境.mp4", "ext": ".mp4", "category": "Lua" }, { "type": "video", "content": "", "intent": "app_future", "title": "關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考", "order": 7, "local_video_path": "videos/Lua/關(guān)于混合應(yīng)用開(kāi)發(fā)的未來(lái)的一些思考.mp4", "ext": ".mp4", "category": "Lua" }, { "type": "video", "content": "", "intent": "google_bug", "title": "記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug", "order": 8, "local_video_path": "videos/Lua/記錄我發(fā)現(xiàn)的第一個(gè)關(guān)于 Google 的 Bug.mp4", "ext": ".mp4", "category": "Lua" }, { "type": "video", "content": "", "intent": "tip_rest", "title": "【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘", "order": 9, "local_video_path": "videos/frp/【樹(shù)莓派自動(dòng)化應(yīng)用實(shí)例】整點(diǎn)提醒自己休息五分鐘.mp4", "ext": ".mp4", "category": "frp" }, { "type": "video", "content": "", "intent": "frp_anywhere", "title": "借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派", "order": 10, "local_video_path": "videos/frp/借助 frp 隨時(shí)隨地訪問(wèn)自己的樹(shù)莓派.mp4", "ext": ".mp4", "category": "frp" } ]參考文章
【趣味連載】攻城獅上傳視頻與普通人上傳視頻 源碼工程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/11044.html
摘要:還有就是,我拿到的第一手?jǐn)?shù)據(jù),也不是什么結(jié)構(gòu)化數(shù)據(jù),而是一個(gè)表格,和一個(gè)分散在多個(gè)文件夾下的視頻資源。原來(lái)只有約個(gè)視頻,都是交給普通人上傳的。一生成結(jié)構(gòu)化數(shù)據(jù)講述的是,數(shù)據(jù)如何從普通的文檔數(shù)據(jù),變成最終可被程序化處理的過(guò)程。 前言 我想寫(xiě)一個(gè)簡(jiǎn)單的系列文章。主題很簡(jiǎn)單,就是記錄下面對(duì)上傳視頻需求時(shí),攻城獅和普通人(泛指所有非技術(shù)人員)的一些區(qū)別。當(dāng)然,從需求分析到最終完整實(shí)現(xiàn),每個(gè)步驟...
摘要:還有就是,我拿到的第一手?jǐn)?shù)據(jù),也不是什么結(jié)構(gòu)化數(shù)據(jù),而是一個(gè)表格,和一個(gè)分散在多個(gè)文件夾下的視頻資源。原來(lái)只有約個(gè)視頻,都是交給普通人上傳的。一生成結(jié)構(gòu)化數(shù)據(jù)講述的是,數(shù)據(jù)如何從普通的文檔數(shù)據(jù),變成最終可被程序化處理的過(guò)程。 前言 我想寫(xiě)一個(gè)簡(jiǎn)單的系列文章。主題很簡(jiǎn)單,就是記錄下面對(duì)上傳視頻需求時(shí),攻城獅和普通人(泛指所有非技術(shù)人員)的一些區(qū)別。當(dāng)然,從需求分析到最終完整實(shí)現(xiàn),每個(gè)步驟...
閱讀 3109·2021-09-03 10:33
閱讀 1322·2019-08-30 15:53
閱讀 2678·2019-08-30 15:45
閱讀 3440·2019-08-30 14:11
閱讀 596·2019-08-30 13:55
閱讀 2689·2019-08-29 15:24
閱讀 1992·2019-08-26 18:26
閱讀 3627·2019-08-26 13:41