整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          數(shù)據(jù)血緣圖譜升級方案設(shè)計與實現(xiàn)

          據(jù)地圖平臺是字節(jié)跳動內(nèi)部的大數(shù)據(jù)檢索平臺,每天近萬的字節(jié)員工在此查找所需數(shù)據(jù)。數(shù)據(jù)地圖通過提供便捷的找數(shù),理解數(shù)服務(wù),大大節(jié)省了內(nèi)部數(shù)據(jù)的溝通和建設(shè)成本。

          數(shù)據(jù)血緣圖譜介紹

          字節(jié)的數(shù)據(jù)可分為端數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù),這些記錄往往需要通過加工處理才能產(chǎn)生業(yè)務(wù)價值。數(shù)據(jù)加工處理的流程一般是讀取原始數(shù)據(jù),進(jìn)行數(shù)據(jù)清洗,再經(jīng)過多種計算和存儲,最終匯入指標(biāo)、報表和數(shù)據(jù)服務(wù)系統(tǒng)。數(shù)據(jù)血緣描述了數(shù)據(jù)的來源和去向,以及數(shù)據(jù)在多個處理過程中的轉(zhuǎn)換,是組織內(nèi)使數(shù)據(jù)發(fā)揮價值的重要基礎(chǔ)能力。

          數(shù)據(jù)地圖平臺在 2021 年接入了全鏈路核心元數(shù)據(jù),包括但不限于:Hive、Clickhouse、Kafka、BI 報表、BI 數(shù)據(jù)集、畫像、埋點、MySQL、Abase。這些數(shù)據(jù)全部要通過數(shù)據(jù)血緣連接起來,進(jìn)而可以進(jìn)行影響分析、內(nèi)部審計、SLA 保障、歸因分析、理解和查找數(shù)據(jù)、自動化推薦等操作。

          隨著內(nèi)部數(shù)據(jù)不斷膨脹,簡單的數(shù)據(jù)血緣圖譜已經(jīng)無法滿足萬級表血緣的關(guān)系展示。一些突出的問題包括看不清單個表的直接上下游,看不清數(shù)據(jù)鏈路,整體情況等等。因此需要重構(gòu)一種更清晰、靈活、便利的方式。下圖簡單展示了優(yōu)化后的使用效果。

          在新版血緣圖譜中,我們可以直接清晰的看到每個表的多層上下游依賴關(guān)系,甚至可以直接看到一些特殊場景下用戶關(guān)注的表屬性,通過點擊節(jié)點高亮查看數(shù)據(jù)鏈路,更可以看清每層的統(tǒng)計信息。在下文中我們將詳細(xì)拆解優(yōu)化的全過程。

          需求發(fā)現(xiàn)

          要做出一個能滿足用戶需求的圖產(chǎn)品,首先是要清楚用戶想從圖中獲取什么信息,從而有針對性的將這些信息展示出來。從血緣圖譜的背景本身可以推斷出用戶希望在圖譜中查看表之間的關(guān)系,查看關(guān)系鏈路,而更多的使用場景待發(fā)掘。因此我們對內(nèi)部重度用戶進(jìn)行了訪談,整理得出了以下不同用戶角色使用數(shù)據(jù)血緣圖譜的用戶場景。

          結(jié)合訪談結(jié)果和用戶的日常反饋,數(shù)據(jù)血緣圖譜的場景按目前用戶的使用頻率從大到小排序依次為:

          場景

          用戶關(guān)注

          場景描述

          影響分析

          下游

          當(dāng)處于血緣上游的研發(fā)同學(xué)修改任務(wù)前,通過查看自己的下游,通知對應(yīng)資產(chǎn)或任務(wù)的負(fù)責(zé)人,進(jìn)行相應(yīng)的修改,否則會造成嚴(yán)重的生產(chǎn)事故。

          找數(shù)理解數(shù)


          上游

          在找數(shù)據(jù)時,通過查看一份數(shù)據(jù)資產(chǎn)的血緣,來更多的了解它的“前世今生”,可以更好的判定當(dāng)前資產(chǎn)是不是自己需要的,或者是不是值得信賴的。就像了解一個人,可以從他周圍的朋友中得到很多信息一樣,是對這個人“生平”很好的補充。

          鏈路梳理

          鏈路

          事先挑選已知的核心任務(wù),通過血緣關(guān)系,自動化的梳理出其所在的核心鏈路。多用于內(nèi)審和數(shù)據(jù)治理。

          歸因分析

          上游

          當(dāng)某一個指標(biāo)或字段數(shù)據(jù)/產(chǎn)出時間等出問題時,通過查看血緣上游的任務(wù)或資產(chǎn),排查出造成問題的根因。

          使用分析

          下游

          一個表的下游表越多,使用越頻繁,可以認(rèn)為價值越大。

          抽象出幾個主要需求即為:

          1. 表血緣關(guān)系查看:能從圖中清楚的瀏覽用戶關(guān)注的表的上下游血緣關(guān)系,最好還能便捷的查看一些場景相關(guān)的表屬性。
          2. 表血緣鏈路查看能清晰的查看到某個上游/下游表到用戶關(guān)注表的鏈路情況。
          3. 按關(guān)鍵指標(biāo)分組查看例如當(dāng)表數(shù)據(jù)發(fā)生變更時,分組查看所有下游表的負(fù)責(zé)人以便通知變更。
          4. 篩選關(guān)鍵信息查看:例如用戶找數(shù)據(jù)指標(biāo)的時候,僅看相關(guān)的報表更高效。

          問題分析

          其實上述需求舊版血緣圖譜都有一定程度上的滿足,我們需要去找出舊版血緣圖譜提供的功能為什么不滿足用戶需求,有哪些問題需要在新版中注意避免。

          • 概覽:在數(shù)據(jù)量較小的情況下可用,在數(shù)據(jù)量大的時候完全不可用。看不清每層有多少個節(jié)點,層級關(guān)系是怎么樣的,且鏈路查看困難。

          節(jié)點較少,比較清晰

          大量節(jié)點,查看困難

          • 舊版血緣圖譜中功能細(xì)節(jié)粗糙:
            • 用戶無法直觀的區(qū)分節(jié)點:舊版節(jié)點上顯示了表類型、庫名、表名。因此表名只能顯示幾個字符,不具備辨識度。
            • 無法知曉表到表之間的任務(wù):舊版血緣圖譜僅在側(cè)邊欄列出了與當(dāng)前表相關(guān)的任務(wù)有哪些并未列出加工邏輯的對應(yīng)關(guān)系,歸因分析困難。
            • 分組結(jié)構(gòu)不清晰:舊版是在原圖中框出節(jié)點來展示分組的。一方面是空間利用率更低,另一方面是看節(jié)點時難定位到所屬分組,看分組時則無法看清包含的節(jié)點。
            • 篩選功能不直觀:符合篩選條件的節(jié)點高亮展示,而被篩掉的表仍在圖中,無法有效提升用戶瀏覽效率。

          方案設(shè)計

          用戶在使用過程中看重的是查看關(guān)系的效率屬性的完備度,因此在設(shè)計優(yōu)化方案時會盡量從這兩點出發(fā)去考慮。

          首先是表數(shù)據(jù)查看的效率問題。看不清表名,無法區(qū)分相同前綴的表是用戶痛點之一。首先我們統(tǒng)計了現(xiàn)有表的平均字符數(shù)是 47 位,于是調(diào)寬了節(jié)點讓用戶能更直觀的區(qū)分表名。用數(shù)據(jù)地圖平臺中通用的類型圖表來代替色塊圖例,讓數(shù)據(jù)類型一目了然。

          其次對于數(shù)據(jù)量大時看不清數(shù)據(jù)關(guān)系的問題,我們需要一個更緊湊清晰的數(shù)據(jù)呈現(xiàn)方式。通過需求分析和用戶調(diào)研,我們了解到用戶關(guān)心的是節(jié)點所在層級和節(jié)點之間的聯(lián)系。對于同一層級節(jié)點的先后順序,層級節(jié)點之間的關(guān)系不是很看重。

          說到緊湊的布局方式,自然而然我們就想到了列表。如果能用一個列表來承載層級血緣的節(jié)點,用連線來連接不同層級的節(jié)點,那么久可以表達(dá)節(jié)點之間的血緣關(guān)系了。當(dāng)節(jié)點較多超出一屏?xí)r可以拖動此列滾動條來查看更多節(jié)點,連線隨之刷新位置。當(dāng)層級不滿一屏?xí)r整體居中展示,層級過多超過一屏?xí)r可以左右滑動查看。這樣在保留層級結(jié)構(gòu)信息的同時最大程度的利用了可視區(qū)域,展示出了盡可能多的數(shù)據(jù)。

          新版血緣圖譜支持了點擊任意節(jié)點則高亮該節(jié)點到主節(jié)點的鏈路功能。配合列滾動和連線刷新,不管數(shù)據(jù)量多大總能看清一整條數(shù)據(jù)鏈路。

          我們還在每列列表頂部增加了層級信息和節(jié)點統(tǒng)計,讓用戶能同時查看每個節(jié)點細(xì)節(jié)和節(jié)點的整體分布。最終實現(xiàn)效果如下圖:

          當(dāng)用戶想去找數(shù),理解數(shù)或做歸因分析時,不僅要了解一個表的上游依賴,更需要理解表的加工邏輯。因此我們在節(jié)點的連線上新增了任務(wù)信息。當(dāng)用戶 hover 到連線上后,連線會加粗高亮并彈出任務(wù)信息。我們還附上了大數(shù)據(jù)開發(fā)平臺的對應(yīng)任務(wù)鏈接,點擊鏈接即可跳轉(zhuǎn)到新頁面查看任務(wù)邏輯詳情。

          在設(shè)計分組功能時,采用了每列獨立分組的方式。一般認(rèn)為用戶會關(guān)注有對應(yīng)分組數(shù)據(jù)的節(jié)點,因此總將有分組的數(shù)據(jù)放在上面,無分組數(shù)據(jù)的置底,這樣排序能提升用戶的瀏覽效率。

          舊版血緣圖譜的篩選功能是在前端處理的,由于一些性能限制導(dǎo)致篩選后只能顯示部分?jǐn)?shù)據(jù),用戶無法得知符合條件的節(jié)點是否已經(jīng)全部展示。新版血緣圖譜針對這個用戶痛點,將前端篩選改為了服務(wù)端篩選,盡量展示全符合要求的數(shù)據(jù)。每個層級的頂欄對應(yīng)更新為篩選后的統(tǒng)計信息。同時更新連線,如果篩選后節(jié)點之間是有關(guān)聯(lián)的,也會展示關(guān)聯(lián)關(guān)系和高亮關(guān)系鏈路。

          不同職能的用戶在不同場景下使用血緣圖譜時關(guān)注的節(jié)點屬性并不相同,如果血緣圖譜可以直接在圖上顯示用戶當(dāng)前想關(guān)注的表屬性就能幫助用戶更高效的解決問題。于是我們在血緣圖譜上設(shè)計了屬性展示功能,用戶可以勾選自己感興趣的屬性直接顯示到圖中。比如下圖中展示了每個節(jié)點表熱度和生命周期兩個屬性。

          技術(shù)實現(xiàn)

          技術(shù)選型

          在編碼實現(xiàn)之前,我們需要進(jìn)行技術(shù)選型。好的選型往往能讓編碼事半功倍。在做技術(shù)選型時,我們會主要考慮實現(xiàn)復(fù)雜度、研發(fā)周期、可擴展性三個角度。分析整個血緣圖譜的需求:

          1. Canvas 實現(xiàn)滾動條,節(jié)點文字標(biāo)簽混排很復(fù)雜,要達(dá)到 HTML 的美觀度需要大量調(diào)試,后續(xù)迭代要新增屬性標(biāo)簽,進(jìn)行流式布局會很頭痛。開放組件給別的產(chǎn)品復(fù)用也有很大的定制成本。而這些問題使用 React 框架渲染就可以輕松解決。
          2. 如果用 DOM 實現(xiàn)不但很難實現(xiàn)箭頭,在連線高亮?xí)r也很難靈活處理層疊關(guān)系。在大數(shù)據(jù)量下連線很多,還容易出現(xiàn)性能問題。而這是 Canvas 的優(yōu)勢。

          于是我們結(jié)合兩者之長,選用了 React + Canvas 的混合模式來實現(xiàn)血緣圖譜。Canvas 居于底部,僅負(fù)責(zé)畫連線。React 在上層負(fù)責(zé)渲染節(jié)點響應(yīng) hover 等交互。DOM 層疊關(guān)系如下:

          整個血緣圖譜的初始化流程如下:

          • 數(shù)據(jù)預(yù)處理服務(wù)端給到點邊結(jié)構(gòu)的數(shù)據(jù)。由于兩個節(jié)點之間可能存在多個任務(wù),對應(yīng)會有多條連線記錄。而血緣圖譜中相同兩個節(jié)點之間僅一條連線,對應(yīng)多個任務(wù)。先做連線的合并處理。
          • 計算節(jié)點層級服務(wù)端會給到點邊結(jié)構(gòu)的數(shù)據(jù),根據(jù)主節(jié)點的連線關(guān)系向來源和去向兩個方向做廣度遍歷來確定每個節(jié)點的層級。
          • 數(shù)據(jù)分組按分組條件對每列數(shù)據(jù)進(jìn)行分組計算。
          • 節(jié)點布局根據(jù)層級和分組情況布局節(jié)點,相對應(yīng)的每個節(jié)點有 { x, y, width, height 屬性以確定每個節(jié)點的定位。
          • 初始化畫布畫布用于繪制連線,響應(yīng)連線的交互。采用內(nèi)部自研的圖形渲染引擎實現(xiàn)。
          • 渲染節(jié)點根據(jù)節(jié)點的位置和分組情況用 React 渲染出每一列節(jié)點 DOM。
          • 渲染畫布根據(jù)前景的列和節(jié)點位置調(diào)整畫布,繪制連線。在渲染連線時分兩個圖層:默認(rèn)狀態(tài)連線在底層;高亮鏈路和高亮連線狀態(tài)下的連線在上層。這樣做的好處是高亮的連線永遠(yuǎn)在默認(rèn)狀態(tài)的上方,不用特殊處理圖形的層疊關(guān)系。

          實現(xiàn)細(xì)節(jié)

          用這種混合模式的一個挑戰(zhàn)就是 Canvas 和 DOM 的刷新率和同步率。在血緣圖譜中滾動橫向滾動條和每一列的縱向滾動條時 Canvas 要進(jìn)行及時的刷新以保證連線和節(jié)點的相對位置一定。

          • 當(dāng)圖譜橫向滾動時,每條連線的斜率不變,只是端點左右平移了。我們可以通過更新繪圖矩陣來加速這種情況下的更新,不需要去重計算每條連線的位置。具體做法是監(jiān)聽容器的滾動事件,根據(jù)容器的 scrollLeft 屬性來更新繪圖矩陣后重繪。
          • 當(dāng)圖譜縱向滾動時,與當(dāng)前滾動的列中節(jié)點相連的連線斜率和端點都有變化,而與滾動列不直接相連的連線無需更新。我們僅重計算并更新與當(dāng)前列連接的線條位置。

          另一個挑戰(zhàn)是 DOM 節(jié)點在大數(shù)據(jù)量下的性能問題。通常情況下我們認(rèn)為 Canvas 在大數(shù)據(jù)量渲染有更好的性能,而萬級的 DOM 節(jié)點就會讓用戶在使用中感受到卡頓了。這時候我們想到了按需渲染。 用戶在圖譜可視區(qū)域中一屏能看到的節(jié)點數(shù)量是有限的,高度為 1120 的容器中,一列僅存在至多 30 個節(jié)點。如果僅渲染可見的節(jié)點,則能保證使用過程的流暢。具體做法是在節(jié)點布局時增加以下步驟:

          • 根據(jù)視口的位置(主要是圖容器的橫向滾動距離 scrollLeft )和每一列的滾動距離(主要是每一列容器的縱向滾動距離 scrollTop )計算目前的可視范圍。
          • 計算節(jié)點坐標(biāo)時判斷是否在可視范圍的上半屏和下半屏內(nèi),如果在此范圍內(nèi)則打標(biāo)。多顯示一屏的節(jié)點是希望在用戶上下滾動瀏覽節(jié)點時不會出現(xiàn)空白區(qū)域閃一下等體驗不佳的問題。
          • 計算出每一列的真實長度。

          在 React 渲染時更新每列容器的長度,將節(jié)點根據(jù)坐標(biāo)絕對定位到正確的位置上。看起來就跟全量渲染的效果一致,渲染效率大幅提升。

          然而問題并不止于此。在進(jìn)行大數(shù)據(jù)量的縱向滾動時,會發(fā)現(xiàn)幀率很低,交互還是不流暢。分析得知是由于列表滾動時會在短時間內(nèi)進(jìn)行大量線條重計算和渲染。于是還要在 Canvas 繪制上進(jìn)行優(yōu)化。

          我們從上圖可以看到在單層節(jié)點很多的情況下,主節(jié)點與不可見節(jié)點的連線可見,但是沒有任何價值,只是加重了用戶對當(dāng)前節(jié)點連線查看的負(fù)擔(dān)。因此我們對線條也進(jìn)行了渲染優(yōu)化,僅當(dāng)一條連線兩端的節(jié)點都在可見范圍中時才渲染連線,在連線的 Tooltip 上增加了來源去向的展示輔助查看。至此我們做到了在復(fù)雜情況下的流暢展示血緣數(shù)據(jù)。


          總結(jié)

          以上就是數(shù)據(jù)血緣圖譜的整個優(yōu)化過程。在這個過程中,我總結(jié)起來就是在了解用戶訴求的前提下,克制地表達(dá)關(guān)系圖中的信息,在合適的場景下突出核心的內(nèi)容做圖分析產(chǎn)品時不需要拘泥于某種形式,而是真正的從用戶需求出發(fā),為用戶服務(wù)。

          關(guān)于我們

          火山引擎大數(shù)據(jù)研發(fā)治理套件 DataLeap

          一站式數(shù)據(jù)中臺套件,幫助用戶快速完成數(shù)據(jù)集成、開發(fā)、運維、治理、資產(chǎn)、安全等全套數(shù)據(jù)中臺建設(shè),幫助數(shù)據(jù)團隊有效的降低工作成本和數(shù)據(jù)維護成本、挖掘數(shù)據(jù)價值、為企業(yè)決策提供數(shù)據(jù)支撐。

          歡迎加入字節(jié)跳動數(shù)據(jù)平臺官方群,進(jìn)行數(shù)據(jù)技術(shù)交流、獲取更多內(nèi)容干貨

          點擊 大數(shù)據(jù)研發(fā)治理套件-火山引擎 了解產(chǎn)品詳情



          在前面

          今年國慶假期終于可以憋在家里了不用出門了,不用出去看后腦了,真的是一種享受。這么好的光陰怎么浪費,睡覺、吃飯、打豆豆這怎么可能(耍多了也煩),完全不符合我們程序員的作風(fēng),趕緊起來把文章寫完。

          這篇文章比較基礎(chǔ),在國慶期間的業(yè)余時間寫的,這幾天又完善了下,力求把更多的前端所涉及到的關(guān)于文件上傳的各種場景和應(yīng)用都涵蓋了,若有疏漏和問題還請留言斧正和補充。

          自測讀不讀

          以下是本文所涉及到的知識點,break or continue ?

          • 文件上傳原理
          • 最原始的文件上傳
          • 使用 koa2 作為服務(wù)端寫一個文件上傳接口
          • 單文件上傳和上傳進(jìn)度
          • 多文件上傳和上傳進(jìn)度
          • 拖拽上傳
          • 剪貼板上傳
          • 大文件上傳之分片上傳
          • 大文件上傳之?dāng)帱c續(xù)傳
          • node 端文件上傳

          原理概述

          原理很簡單,就是根據(jù) http 協(xié)議的規(guī)范和定義,完成請求消息體的封裝和消息體的解析,然后將二進(jìn)制內(nèi)容保存到文件。

          我們都知道如果要上傳一個文件,需要把 form 標(biāo)簽的enctype設(shè)置為multipart/form-data,同時method必須為post方法。

          那么multipart/form-data表示什么呢?

          multipart互聯(lián)網(wǎng)上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。

          multipart/form-data 結(jié)構(gòu)

          看下 http 請求的消息體



          • 請求頭:

          Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次請求要上傳文件,其中boundary表示分隔符,如果要上傳多個表單項,就要使用boundary分割,每個表單項由———XXX開始,以———XXX結(jié)尾。

          • 消息體- Form Data 部分

          每一個表單項又由Content-Type和Content-Disposition組成。

          Content-Disposition: form-data 為固定值,表示一個表單元素,name 表示表單元素的 名稱,回車換行后面就是name的值,如果是上傳文件就是文件的二進(jìn)制內(nèi)容。

          Content-Type:表示當(dāng)前的內(nèi)容的 MIME 類型,是圖片還是文本還是二進(jìn)制數(shù)據(jù)。

          解析

          客戶端發(fā)送請求到服務(wù)器后,服務(wù)器會收到請求的消息體,然后對消息體進(jìn)行解析,解析出哪是普通表單哪些是附件。

          可能大家馬上能想到通過正則或者字符串處理分割出內(nèi)容,不過這樣是行不通的,二進(jìn)制buffer轉(zhuǎn)化為string,對字符串進(jìn)行截取后,其索引和字符串是不一致的,所以結(jié)果就不會正確,除非上傳的就是字符串。

          不過一般情況下不需要自行解析,目前已經(jīng)有很成熟的三方庫可以使用。

          至于如何解析,這個也會占用很大篇幅,后面的文章在詳細(xì)說。

          最原始的文件上傳

          使用 form 表單上傳文件

          在 ie時代,如果實現(xiàn)一個無刷新的文件上傳那可是費老勁了,大部分都是用 iframe 來實現(xiàn)局部刷新或者使用 flash 插件來搞定,在那個時代 ie 就是最好用的瀏覽器(別無選擇)。

          DEMO



          這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗很差,導(dǎo)致頁面刷新,頁面其他數(shù)據(jù)丟失。

          HTML

           <form method="post" action="http://localhost:8100" enctype="multipart/form-data">
          
                  選擇文件:
                      <input type="file" name="f1"/> input 必須設(shè)置 name 屬性,否則數(shù)據(jù)無法發(fā)送<br/>
          <br/>
                      標(biāo)題:<input type="text" name="title"/><br/><br/><br/>
          
                  <button type="submit" id="btn-0">上 傳</button>
          
          </form>
          
          復(fù)制代碼

          文件上傳接口

          服務(wù)端文件的保存基于現(xiàn)有的庫koa-body結(jié)合 koa2實現(xiàn)服務(wù)端文件的保存和數(shù)據(jù)的返回。

          在項目開發(fā)中,文件上傳本身和業(yè)務(wù)無關(guān),代碼基本上都可通用。

          在這里我們使用koa-body庫來實現(xiàn)解析和文件的保存。

          koa-body 會自動保存文件到系統(tǒng)臨時目錄下,也可以指定保存的文件路徑。



          然后在后續(xù)中間件內(nèi)得到已保存的文件的信息,再做二次處理。

          • ctx.request.files.f1 得到文件信息,f1為input file 標(biāo)簽的 name
          • 獲得文件的擴展名,重命名文件

          NODE

          /**
           * 服務(wù)入口
           */
          var http = require('http');
          var koaStatic = require('koa-static');
          var path = require('path');
          var koaBody = require('koa-body');//文件保存庫
          var fs = require('fs');
          var Koa = require('koa2');
          
          var app = new Koa();
          var port = process.env.PORT || '8100';
          
          var uploadHost= `http://localhost:${port}/uploads/`;
          
          app.use(koaBody({
              formidable: {
                  //設(shè)置文件的默認(rèn)保存目錄,不設(shè)置則保存在系統(tǒng)臨時目錄下  os
                  uploadDir: path.resolve(__dirname, '../static/uploads')
              },
              multipart: true // 開啟文件上傳,默認(rèn)是關(guān)閉
          }));
          
          //開啟靜態(tài)文件訪問
          app.use(koaStatic(
              path.resolve(__dirname, '../static') 
          ));
          
          //文件二次處理,修改名稱
          app.use((ctx) => {
              var file = ctx.request.files.f1;//得道文件對象
              var path = file.path;
              var fname = file.name;//原文件名稱
              var nextPath = path+fname;
              if(file.size>0 && path){
                  //得到擴展名
                  var extArr = fname.split('.');
                  var ext = extArr[extArr.length-1];
                  var nextPath = path+'.'+ext;
                  //重命名文件
                  fs.renameSync(path, nextPath);
              }
              //以 json 形式輸出上傳文件地址
              ctx.body = `{
                  "fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
              }`;
          });
          
          /**
           * http server
           */
          var server = http.createServer(app.callback());
          server.listen(port);
          console.log('demo1 server start ......   ');
          復(fù)制代碼

          CODE

          https://github.com/Bigerfe/fe-learn-code/

          TML 字符集

          如需正確地顯示 HTML 頁面,瀏覽器必須知道使用何種字符集。

          萬維網(wǎng)早期使用的字符集是 ASCII。ASCII 支持 0-9 的數(shù)字,大寫和小寫英文字母表,以及一些特殊字符。

          由于很多國家使用的字符并不屬于 ASCII,現(xiàn)代瀏覽器的默認(rèn)字符集是 ISO-8859-1。

          如果網(wǎng)頁使用不同于 ISO-8859-1 的字符集,就應(yīng)該在 <meta> 標(biāo)簽進(jìn)行指定。

          ISO 字符集

          ISO 字符集是國際標(biāo)準(zhǔn)組織 (ISO) 針對不同的字母表/語言定義的標(biāo)準(zhǔn)字符集。

          下面列出了世界各地使用的不同字符集:

          字符集描述使用范圍
          ISO-8859-1Latin alphabet part 1北美、西歐、拉丁美洲、加勒比海、加拿大、非洲
          ISO-8859-2Latin alphabet part 2東歐
          ISO-8859-3Latin alphabet part 3SE Europe、世界語、其他雜項
          ISO-8859-4Latin alphabet part 4斯堪的納維亞/波羅的海(以及其他沒有包括在 ISO-8859-1 中的部分)
          ISO-8859-5Latin/Cyrillic part 5使用古代斯拉夫語字母表的語言,比如保加利亞語、白俄羅斯文、俄羅斯語、馬其頓語
          ISO-8859-6Latin/Arabic part 6使用阿拉伯字母的語言
          ISO-8859-7Latin/Greek part 7現(xiàn)代希臘語,以及由希臘語衍生的數(shù)學(xué)符號
          ISO-8859-8Latin/Hebrew part 8使用希伯來語的語言
          ISO-8859-9Latin 5 part 9土耳其語。除了土耳其字符取代了冰島文字,其它與 ISO-8859-1 相同。
          ISO-8859-10Latin 6拉普蘭語、日耳曼語、愛斯基摩北歐語
          ISO-8859-15Latin 9 (aka Latin 0)與 ISO 8859-1 類似,歐元符號和其他一些字符取代了一些較少使用的符號
          ISO-2022-JPLatin/Japanese part 1日本語
          ISO-2022-JP-2Latin/Japanese part 2日本語
          ISO-2022-KRLatin/Korean part 1韓語

          Unicode 標(biāo)準(zhǔn)

          由于上面列出的字符集都有容量限制,而且不兼容多語言環(huán)境,Unicode 聯(lián)盟開發(fā)了 Unicode 標(biāo)準(zhǔn)。

          Unicode 標(biāo)準(zhǔn)涵蓋了世界上的所有字符、標(biāo)點和符號。

          不論是何種平臺、程序或語言,Unicode 都能夠進(jìn)行文本數(shù)據(jù)的處理、存儲和交換。

          Unicode 聯(lián)盟

          Unicode 聯(lián)盟開發(fā)了 Unicode 標(biāo)準(zhǔn)。他們的目標(biāo)是用標(biāo)準(zhǔn)的 Unicode 轉(zhuǎn)換格式 (UTF) 來取代現(xiàn)有的字符集。

          Unicode 標(biāo)準(zhǔn)已經(jīng)獲得了成功,在 XML、Java、ECMAScript (JavaScript)、LDAP、CORBA 3.0、WML 中,Unicode 已經(jīng)得到了實現(xiàn)。在許多操作系統(tǒng)以及所有的現(xiàn)代瀏覽器中,Unicode 同樣得到了支持。

          Unicode 聯(lián)盟與領(lǐng)導(dǎo)性的標(biāo)準(zhǔn)發(fā)展組織進(jìn)行合作,比如 ISO、W3C 以及 ECMA。

          Unicode 可以被不同的字符集兼容。最常用的編碼方式是 UTF-8 和 UTF-16:

          字符集描述
          UTF-8UTF8 中的字符可以是 1-4 個字節(jié)長。UTF-8 可以表示 Unicode 標(biāo)準(zhǔn)中的任意字符。UTF-8 向后兼容 ASCII。UTF-8 是網(wǎng)頁和電子郵件的首選編碼。
          UTF-1616 比特的 Unicode 轉(zhuǎn)換格式是一種 Unicode 可變字符編碼,能夠?qū)θ?Unicode 指令表進(jìn)行編碼。UTF-16 主要被用于操作系統(tǒng)和環(huán)境中,比如微軟的 Windows 2000/XP/2003/Vista/CE 以及 Java 和 .NET 字節(jié)代碼環(huán)境。

          提示: 最前面的 256 個 Unicode 字符集字符對應(yīng)于 256 個 ISO-8859-1 字符。

          提示: 所有 HTML 4 處理器均已支持 UTF-8,而所有 XHTML 和 XML 處理器支持 UTF-8 和 UTF-16!

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!


          主站蜘蛛池模板: 日韩人妻无码一区二区三区99| 波多野结衣一区视频在线| 亚洲蜜芽在线精品一区| 国产免费一区二区三区VR| 国产伦精品一区二区三区| 无码人妻精品一区二区三区9厂 | 久久久老熟女一区二区三区| 精品国产毛片一区二区无码| 国产天堂在线一区二区三区| 日本无卡码一区二区三区| 日韩av无码一区二区三区| 制服丝袜一区二区三区| 中文字幕在线视频一区| 国产福利电影一区二区三区久久久久成人精品综合 | 一区二区三区在线视频播放| 亚洲字幕AV一区二区三区四区| 无码国产精品一区二区免费式芒果| 精品视频在线观看你懂的一区| 日本精品一区二区三区四区| 国产婷婷色一区二区三区深爱网| 波多野结衣AV一区二区三区中文| 男插女高潮一区二区| 无码人妻精品一区二区蜜桃 | 熟妇人妻AV无码一区二区三区| 成人精品视频一区二区三区不卡 | 国产在线精品一区二区在线看 | 国产SUV精品一区二区88L | 人妻少妇精品一区二区三区| 一区二区三区www| 中文字幕不卡一区| 亚洲乱码国产一区三区| 久久精品岛国av一区二区无码| 午夜天堂一区人妻| 日韩国产免费一区二区三区| 女人18毛片a级毛片一区二区| 日韩三级一区二区| 亚洲AV永久无码精品一区二区国产| 亚洲视频在线一区二区| 中日av乱码一区二区三区乱码| 久久青草国产精品一区| 无码av人妻一区二区三区四区|