盤實戰(zhàn):SSH框架+Java API實現(xiàn)與避坑指南)
簡介分布式文件系統(tǒng)是大數(shù)據(jù)存儲的基礎設施HDFS作為Hadoop生態(tài)的核心組件通過NameNode和DataNode的協(xié)作實現(xiàn)海量文件的可靠存儲與讀寫。在Java Web開發(fā)中基于SSH框架Struts2SpringHibernate構建的HDFS網(wǎng)盤項目既是對HDFS編程接口的綜合實訓也是數(shù)據(jù)管理場景的經(jīng)典實踐。這類項目通常采用偽分布式環(huán)境用Java API封裝上傳、下載、目錄管理等操作并需解決Windows開發(fā)與Linux部署的差異問題。從HDFS讀寫流程到FileSystem實例的正確使用再到部署中的高頻報錯排查掌握這些關鍵技術點能幫助開發(fā)者快速實現(xiàn)一個可用于課程設計或工程演示的網(wǎng)盤系統(tǒng)同時深入理解分布式文件系統(tǒng)與Web應用的集成方式。1. 這個Hadoop網(wǎng)盤項目到底是什么先別急著解壓課程設計里最常出現(xiàn)的一個名場面就是你下載到一個叫“基于hadoop利用ssh框架實現(xiàn)hdfs網(wǎng)盤.zip”的項目包解壓后一堆源碼卻不知道它到底還能不能跑起來。簡單說這是一個在Hadoop分布式文件系統(tǒng)HDFS之上套了一層Java Web外殼的網(wǎng)盤DemoSSH框架Struts2SpringHibernate負責登錄、頁面跳轉和業(yè)務調度頁面上的上傳、下載、建目錄、刪文件最終都通過Hadoop Java API打到HDFS上。它解決的是“把文件從瀏覽器搬到HDFS再從HDFS取回來”這一整條鏈路適合課程設計、畢業(yè)設計和大數(shù)據(jù)培訓里的HDFS編程綜合實訓。它能跑但不是拿來就能一鍵啟動先別急著解壓得把環(huán)境選型和調用關系看清楚。2. 動手前先立骨架偽分布式環(huán)境、SSH框架選型和HDFS讀寫路徑2.1 為什么偽分布式夠用開發(fā)效率與成本的權衡這類項目包十有八九是給你配好的偽分布式環(huán)境。偽分布式指的是NameNode、DataNode、SecondaryNameNode都在同一臺機器上跑進程分開但數(shù)據(jù)不跨機器。對于“HDFS網(wǎng)盤”這種偏接口開發(fā)的題目偽分布式足夠你能完整體驗HDFS讀寫、Shell命令、Java API又不用折騰多臺機器的網(wǎng)絡與免密配置出問題也只在單臺機器上排查。課程設計或答辯演示通常不會要求你證明“集群能扛并發(fā)”而是要求講清楚讀寫流程和界面功能所以偽分布式是投入產(chǎn)出比最高的選擇。Hadoop的安裝配置我一般推薦一個固定套路下載二進制包JAVA_HOME配好core-site.xml里指定NameNode地址hdfs-site.xml里指定副本數(shù)與元數(shù)據(jù)路徑然后配置shh localhost免密、格式化NameNode、啟動HDFS。核心配置如下。!-- core-site.xml -- configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name value/home/hadoop/data/tmp/value /property /configuration!-- hdfs-site.xml -- configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name valuefile:///home/hadoop/data/name/value /property property namedfs.datanode.data.dir/name valuefile:///home/hadoop/data/data/value /property /configurationfs.defaultFS是后面Java客戶端默認連的地址hadoop.tmp.dir決定了HDFS格式化后的文件落在哪里。這個路徑一定不要放在系統(tǒng)臨時目錄否則重啟系統(tǒng)被清掉NameNode啟動時會找不到元數(shù)據(jù)這也是后面SafeMode反復出現(xiàn)的一個根源。dfs.replication在偽分布式下只能填1因為只有一個DataNode填3的話所有塊都會永遠處于under-replicated狀態(tài)界面上一堆紅色告警。配置好之后的啟動命令基本是固定的hdfs namenode -format start-dfs.sh jpsjps輸出里如果能看到NameNode、DataNode和SecondaryNameNode說明HDFS進程已經(jīng)起來了。從這里往后所有代碼里的默認HDFS地址都應該和core-site.xml保持一致。如果你是在Windows上用IDEA連遠程Linux里跑著的Hadoop下面的localhost要換成Linux機器的IP這一點第4章還會專門展開。我見過不少新手一上來就照著完全分布式教程折騰多節(jié)點集群三臺虛擬機來回搭最后網(wǎng)盤功能只寫了一點點。如果項目標題只是“基于hadoop實現(xiàn)hdfs網(wǎng)盤”偽分布式就夠了。真想上HDFS HA再引入ZooKeeper去整合NameNode主備切換那是另一個工程量。2.2 HDFS讀寫流程網(wǎng)盤上傳下載背后的兩段關鍵路徑要做網(wǎng)盤就必須懂HDFS的讀寫流程否則上傳下載的代碼寫出來只能靠背出了問題也只會重啟重試。先看寫文件??蛻舳苏{用DistributedFileSystem.create這個請求發(fā)給NameNodeNameNode檢查目標路徑是否存在、父目錄是否存在、客戶端是否有權限通過后返回一個可寫的FSDataOutputStream。真正寫數(shù)據(jù)時客戶端把文件切分成數(shù)據(jù)包按流水線方式發(fā)給第一個DataNode這個DataNode再轉發(fā)給第二個、第三個形成一條復制流水線最后一個DataNode寫完后逐級返回確認。網(wǎng)盤里的“上傳”本質上就是這一條寫路徑瀏覽器把文件流交給TomcatTomcat交給HDFS客戶端再進入流水線。再看讀文件??蛻舳苏{用openNameNode不傳數(shù)據(jù)本身只返回文件對應的Block位置列表也就是“這個文件的每個塊在哪些DataNode上”??蛻舳四玫搅斜砗髢?yōu)先就近讀取比如同一機架、同一節(jié)點。網(wǎng)盤里的“下載”和“預覽”就是把這套讀路徑的結果轉成HTTP響應流。所以你會發(fā)現(xiàn)HDFS并不適合存大量小文件因為每個文件每個塊都要在NameNode內存里占一份元數(shù)據(jù)。網(wǎng)盤Demo里文件可能是幾百KB的圖片但如果你要傳幾萬個小文件NameNode內存會先扛不住。這兩個流程在HDFS面試里也常被問到能用自己的話講清楚比背概念更有說服力。項目代碼里如果加了日志你會看到寫文件時客戶端名稱是dfsclient它一直和DataNode交互而NameNode只在元數(shù)據(jù)操作時出現(xiàn)。日常調試HDFS時常用命令也就是這樣一組命令作用備查要點hdfs dfs -ls /列出根目錄目錄權限、文件大小hdfs dfs -mkdir -p /user/hadoop創(chuàng)建多級目錄加-p避免父目錄不存在報錯hdfs dfs -put a.txt /user/hadoop/上傳本地文件也可以從標準輸入寫hdfs dfs -get /user/hadoop/a.txt ./下載到本地下載后務必對比文件大小hdfs dfs -rm -r /user/hadoop遞歸刪除目錄回收站開啟時進Trash網(wǎng)盤代碼里FileSystem對象封裝的就是這些命令對應的Java API。命令行能跑通Java API一定能跑通命令行跑不通Java API大概率也跑不通所以排查問題時先用命令行驗證。2.3 SSH框架在這個項目里的角色Web層到HDFS API的調用鏈標題里的“ssh框架”指的是Struts2、Spring、Hibernate三件套而不是Linux里的ssh命令。很多拿到包的人在這點上會先迷糊一把以為要配SSH免密登錄結果跑去配置用戶認證了。這個項目里Struts2承擔控制器和參數(shù)封裝Spring管理Service、DAO和事務Hibernate主要管理用戶表、分享記錄表這一類關系數(shù)據(jù)。HDFS本身的文件內容不在Hibernate管理范圍內它負責的只是業(yè)務元數(shù)據(jù)。調用鏈按一次上傳操作來看是“瀏覽器multipart表單 → Struts2的FileUpload攔截器把文件轉成臨時File對象 → Action方法名對應struts.xml里的action → Spring容器里的HdfsService → HdfsDao里的FileSystem API → NameNode元數(shù)據(jù)檢查 → DataNode寫入”。這條鏈路里最容易被忽略的是Spring和Struts2的整合Action對象不能直接new否則Spring注入的service全是null。后面第4章的避坑里也會提到。在pom.xml里HDFS客戶端的依賴通常長這樣dependency groupIdorg.apache.hadoop/groupId artifactIdhadoop-client/artifactId version3.3.6/version /dependency這里版本要和Linux上裝的Hadoop保持一致否則客戶端和服務端握手時可能出現(xiàn)RPC協(xié)議版本不匹配報Client cannot communicate with server。Hadoop 2.x客戶端連3.x服務端也常常會失敗建議直接統(tǒng)一用3.3.x。依賴里還會帶出avro、commons-logging等一堆傳遞依賴部署時要注意jar包沖突比如Tomcat自帶的commons-logging版本偏舊可能導致初始化異常。拿到這種zip包先別急著導進IDEA先看目錄是不是Maven工程、有沒有pom。如果沒有pom而是lib目錄說明是Eclipse手動加載jar的寫法。常見的分層是把Action放web包Service放service包HdfsDao放dao包頁面放在WebRoot/pages下。先用文本編輯器打開struts.xml和spring.xml看bean怎么配的再決定從哪里看起。這樣比直接雙擊運行按鈕更能避免“明明代碼沒錯卻起不來”的尷尬。3. 核心實現(xiàn)用Java API把HDFS封裝成網(wǎng)盤接口3.1 拿到FileSystem實例這個“黑匣子”怎么配才穩(wěn)做HDFS網(wǎng)盤一切操作都圍繞FileSystem對象轉。獲取FileSystem最常見的寫法是FileSystem.get(URI.create(hdfsUrl), conf)但放到Web項目里有兩個坑Windows開發(fā)和Linux生產(chǎn)環(huán)境路徑不一樣HDFS用戶身份默認取當前操作系統(tǒng)用戶在Windows上可能是不存在的用戶導致權限校驗直接失敗。我一般會封裝一個HdfsDao把連接參數(shù)抽出來單獨做一個獲取實例的方法public class HdfsDao { private String defaultFs; // 從properties注入例如 hdfs://192.168.1.10:9000 private String hdfsUser; // 一般是Linux上啟動Hadoop的用戶如 hadoop public FileSystem getFileSystem() throws IOException { Configuration conf new Configuration(); conf.set(fs.defaultFS, defaultFs); // Windows開發(fā)機必須指定本地Hadoop二進制目錄否則一堆winutils報錯 conf.set(hadoop.home.dir, D:/soft/hadoop-3.3.6); // 讓客戶端直接按DataNode主機名連接避免NAT和IP解析問題 conf.setBoolean(dfs.client.use.datanode.hostname, true); // 用HDFS服務端的啟動用戶身份訪問繞開權限玄學 return FileSystem.get(URI.create(defaultFs), conf, hdfsUser); } }conf.set(hadoop.home.dir, ...)只在Windows開發(fā)機本地需要Linux上可以不設置或者指向真實安裝目錄。它解決的是Hadoop本地庫和winutils.exe路徑問題不設置就會出現(xiàn)第4章那個經(jīng)典報錯。dfs.client.use.datanode.hostname設為true是當你用IP訪問NameNode而DataNode在另一個內網(wǎng)段時客戶端能拿到主機名而不是內網(wǎng)IP去連接避免返回一個連不通的地址。這里最需要注意的是FileSystem.get是進程范圍內的緩存對象同一個URI和同一個用戶會拿到同一個實例所以這個實例千萬不要隨手close()。你以為只是釋放資源實際上把整個進程里后續(xù)所有請求共用的文件系統(tǒng)對象關掉了第二個人再上傳就會看到FileSystem closed。如果你的代碼里到處都是fs.close()那是把單機文件API的思維帶進來了。3.2 文件上傳本地臨時文件還是流式直傳瀏覽器上傳文件Struts2默認先把文件存入臨時目錄再把臨時目錄路徑和文件名放到Action的File屬性里。第一種做法是把臨時文件直接copy進HDFSpublic void uploadByTempFile(String destDir, String localTempPath) throws IOException { FileSystem fs getFileSystem(); Path src new Path(localTempPath); Path dst new Path(destDir / src.getName()); fs.copyFromLocalFile(false, true, src, dst); }這個寫法最簡單但有明顯問題Web服務器上必須先落一個本地臨時文件文件越大本地IO和臨時目錄壓力越大還要自己清理。第二種做法更貼近網(wǎng)盤場景直接用InputStream往HDFS寫public void upload(InputStream in, String destDir, String fileName) throws IOException { FileSystem fs getFileSystem(); Path dst new Path(destDir / fileName); try (FSDataOutputStream out fs.create(dst, () - { // 進度回調可以在前臺上傳進度條時更新緩存 System.out.println(有數(shù)據(jù)包寫入: System.currentTimeMillis()); })) { IOUtils.copyBytes(in, out, 4096, false); } }fs.create的第二個參數(shù)是Progressable匿名對象HDFS每寫入一個數(shù)據(jù)包就會回調一次。演示時如果要做上傳進度可以在這個回調里記錄已寫入字節(jié)數(shù)。IOUtils.copyBytes來自org.apache.hadoop.io.IOUtils第三個參數(shù)4096是緩沖大小第四個參數(shù)false表示不自動關閉輸入流調用方?jīng)Q定何時關閉。這套寫法不會在Tomcat臨時目錄留下大文件唯一要處理的是文件名路徑合法性比如用戶可能傳入../。所以destDir不要直接拼用戶輸入先做一次路徑歸一化把非法字符過濾掉。3.3 文件下載與預覽亂碼問題的關鍵在于響應頭下載操作本質上是把HDFS里的文件讀出來寫給HttpServletResponse。新手最容易在中文文件名上踩坑直接resp.setHeader(Content-Disposition, attachment; filename fileName)結果下載下來的文件名是亂碼。必須對文件名做URL編碼瀏覽器會自動解碼成原始中文。public void download(String filePath, HttpServletResponse resp) throws IOException { FileSystem fs getFileSystem(); Path path new Path(filePath); FSDataInputStream in fs.open(path); String encodedName URLEncoder.encode(path.getName(), UTF-8); resp.setHeader(Content-Disposition, attachment; filename encodedName); resp.setContentType(application/octet-stream); IOUtils.copyBytes(in, resp.getOutputStream(), 4096, false); }如果要做“圖片預覽”而不是下載把Content-Disposition改成inline并把Content-Type設置成image/png或image/jpeg瀏覽器就會直接展示而不是彈出下載框。判斷依據(jù)可以在文件列表里存一個文件擴展名到MIME類型的Map預覽時按擴展名查。注意fs.open返回的FSDataInputStream支持隨機讀但這里只用順序讀效率沒問題。最后不要在這里關閉FileSystem原因就是前面說的進程級緩存。3.4 目錄列表與刪除重命名給前端返回結構化JSON網(wǎng)盤首頁通常要展示某個目錄下的文件列表用FileSystem.listStatus拿目錄項再轉成前端友好結構public ListMapString, Object listFiles(String dirPath) throws IOException { FileSystem fs getFileSystem(); FileStatus[] statuses fs.listStatus(new Path(dirPath)); ListMapString, Object result new ArrayList(); for (FileStatus status : statuses) { MapString, Object item new HashMap(); item.put(name, status.getPath().getName()); item.put(isDir, status.isDirectory()); item.put(size, status.getLen()); item.put(modTime, status.getModificationTime()); item.put(owner, status.getOwner()); result.add(item); } return result; }FileStatus是HDFS文件系統(tǒng)元信息載體getLen()返回的是文件字節(jié)數(shù)目錄的len是0或一個固定值不能用來判斷大小。前端拿到這個List后渲染成表格點目錄時把當前路徑拼上子目錄名再發(fā)一次請求這就是最簡單的網(wǎng)盤跳轉邏輯。刪除和重命名相對簡單public boolean delete(String path, boolean recursive) throws IOException { FileSystem fs getFileSystem(); return fs.delete(new Path(path), recursive); } public boolean rename(String oldPath, String newPath) throws IOException { FileSystem fs getFileSystem(); return fs.rename(new Path(oldPath), new Path(newPath)); }delete的第二個參數(shù)表示是否遞歸刪除非空目錄。網(wǎng)盤界面上的刪除按鈕如果用戶選的是非空目錄你只傳false會刪不掉提示“目錄不為空”。通常前端會彈出二次確認“是否刪除整個目錄”后端再傳true。真正生產(chǎn)級網(wǎng)盤不會直接物理刪而是把文件移到一個回收站目錄HDFS本身也支持配置Trash回收站開啟后刪除操作會先進回收站而不是立刻釋放空間。常見配置是在core-site.xml里加fs.trash.interval1440單位是分鐘。這個參數(shù)不是每個課程設計都會配但面試時能說出來是加分項。4. 部署與聯(lián)調避坑Windows用IDEA連Linux Hadoop五個高頻翻車點4.1 上傳報錯“Failed to locate DFS nameserver”是fs.defaultFS沒指向Linux現(xiàn)象在Windows上用IDEA跑網(wǎng)盤工程點上傳按鈕頁面報500后臺異常寫著Failed to locate DFS nameserver host: localhost或者是java.net.UnknownHostException。原因Windows本機并沒有運行NameNode而工程里fs.defaultFS還是hdfs://localhost:9000客戶端拿localhost去Windows本機找當然找不到。即便你開了遠程Hadooplocalhost也不是Linux服務器。解決把HDFS地址改成Linux服務器的IP并保證Windows能ping通。方法在properties文件里寫hdfs.defaultFShdfs://192.168.1.10:9000代碼里用System.getProperty(hdfs.defaultFS, ...)讀系統(tǒng)屬性IDEA的Run Configuration里的VM options加一行-Dhdfs.defaultFShdfs://192.168.1.10:9000這樣團隊里每個人不用改代碼。4.2 一啟動就報winutils.exe找不到是Windows本地庫缺失現(xiàn)象在Windows本地調用任何FileSystem方法控制臺報Failed to locate the winutils binary in the Hadoop binaries.或者是Unable to load native-hadoop library for your platform。原因Hadoop客戶端在Windows上運行時需要本地組件winutils.exe用于模擬Unix權限和本地文件操作。Linux服務器上的Hadoop二進制包里并沒有winutils所以光把Linux的hadoop目錄拷到Windows解決不了。解決找一個與你Hadoop版本匹配的winutils壓縮包解壓后把winutils.exe放到本地Hadoop目錄的bin下hadoop.dll放到System32或本地Hadoop的bin同時在代碼里conf.set(hadoop.home.dir, D:/soft/hadoop-3.3.6)。版本不是完全匹配時啟動能過但某些操作會報奇怪的IO錯誤。這一步做完后Windows本地跑HDFS客戶端就和在Linux上一樣了。4.3 Struts2 Action里Service一直為null是對象沒經(jīng)過Spring容器現(xiàn)象項目啟動不報錯點登錄或上傳時拋出NullPointerException代碼斷點在Service調用那一行service對象確實是null。原因struts2.xml里的action的class屬性如果直接填類全名每次請求Struts2都會通過反射new一個Action出來Spring根本來不及注入Resource標注的Service。Spring容器里管理的Action bean反而是閑置的。解決引入struts2-spring-plugin依賴并且struts.xml里action的class寫Spring容器中的bean id而不是類全名。例如action nameupload classhdfsAction result namesuccess/pages/success.jsp/result /actionComponent(hdfsAction) public class HdfsAction extends ActionSupport { Resource private HdfsService hdfsService; }關鍵點是class里填hdfsAction讓Struts2從Spring容器里拿bean。如果填com.example.web.HdfsAction注入仍然是null。這個坑在SSH框架項目里出現(xiàn)頻率極高而且只在運行期暴露寫代碼時根本看不出來。4.4 重啟后上傳報Name node is in safe mode是元數(shù)據(jù)不一致現(xiàn)象Hadoop重啟后上傳文件返回Name node is in safe mode只能讀取不能寫入。原因NameNode啟動時要加載編輯日志和鏡像文件檢查塊報告。如果它發(fā)現(xiàn)DataNode上報的塊和元數(shù)據(jù)不一致會進入安全模式保護數(shù)據(jù)自動等待副本達到最小可用數(shù)。偽分布式副本數(shù)是1機器啟動慢一點就會在安全模式下多停一會。另外如果你反復格式化NameNode但DataNode的data目錄沒清DataNode上報的舊塊信息會讓NameNode更不敢退出安全模式。解決先確認這不是常態(tài)等待一兩分鐘用hdfs dfsadmin -safemode leave手動退出安全模式。如果每次重啟都進安全模式檢查hadoop.tmp.dir是不是被設置到了/tmp是的話改成持久目錄然后清空NameNode和DataNode的data目錄重新hdfs namenode -format。格式化操作在課程設計里允許用但生產(chǎn)環(huán)境不要亂執(zhí)行因為會丟元數(shù)據(jù)。4.5 第二次上傳報FileSystem closed是手動關閉了進程級緩存現(xiàn)象第一次上傳文件到HDFS成功第二次請求時后臺異常里只有一句話FileSystem closed重啟Tomcat后又好一陣過一會兒再犯。原因你的代碼在upload()或download()方法末尾調用了fs.close()。FileSystem.get(URI, conf, user)返回的是JVM緩存中的單例close方法會把這個單例標記為關閉。第二次FileSystem.get拿到的還是那個已經(jīng)關閉的對象于是所有操作全部拒絕。解決刪除方法里所有的fs.close()。如果實在需要釋放資源用FileSystem.newInstance(conf)來創(chuàng)建不緩存的臨時實例用完再close。多數(shù)SSH框架網(wǎng)盤項目不需要頻繁創(chuàng)建銷毀FileSystem一個線程復用一個實例就夠了。另外注意Controller里也不要傳一個已經(jīng)close的FileSystem給Service。這五個坑基本覆蓋了“Windows開發(fā) Linux部署 SSH框架”組合里九成以上的運行期報錯。遇到問題先看Tomcat catalina.out里的異常棧異常里只要帶著hdfs或者Filesystem字樣就回到這幾條里對號入座。5. 驗證與加固讓網(wǎng)盤從“能跑”變成“能演示”5.1 部署后先跑三板斧JPS、report和fsck代碼寫完后不要急著打開瀏覽器點上傳。先在Hadoop服務器上跑一套基礎檢查確定底層沒問題再去看頁面。jps # 偽分布式期望看到NameNode、DataNode、SecondaryNameNode hdfs dfsadmin -report # 查看DataNode在線狀態(tài)、剩余容量、塊副本數(shù) hdfs fsck / -files -blocks -locations # 遍歷根目錄下所有文件列出塊分布檢查損壞塊hdfs fsck輸出的最后兩行會寫Status: HEALTHY或CORRUPT。如果顯示Status: HEALTHY說明HDFS這層沒問題剩下的鍋就在Web工程里。平時維護網(wǎng)盤也建議把fsck當成體檢工具。5.2 用戶目錄隔離和上傳限制演示版也要有邊界開源的課程設計網(wǎng)盤往往沒有用戶目錄的概念所有人傳的文件都堆在根目錄下演示到后面文件亂成一鍋粥。我一般會在用戶注冊成功時給他在HDFS上建一個獨立目錄fs.mkdirs(new Path(/user/ username), new FsPermission((short) 0755));同時在上傳Action里限制單文件大小和擴展名if (file.length() 50L * 1024 * 1024) { throw new RuntimeException(單文件不能超過50MB); } String ext FilenameUtils.getExtension(fileName); SetString allowed new HashSet(Arrays.asList(txt, jpg, png, pdf, zip)); if (!allowed.contains(ext.toLowerCase())) { throw new RuntimeException(不支持的文件類型: ext); }參數(shù)可以根據(jù)演示環(huán)境調重點是不讓瀏覽器傳一個幾個G的臨時文件把HDFS寫滿。演示結束清理數(shù)據(jù)時hdfs dfs -rm -r /user/*一條命令就能清掉所有測試目錄比刪Tomcat臨時目錄省事得多。5.3 用MD5判斷重復上傳最值得做的“秒傳”技巧上傳模塊如果只是繼續(xù)往下寫功能就到此為止了。我會建議你再加一個小技巧在數(shù)據(jù)庫里為每個文件記錄MD5值下次有人上傳相同內容時先比對MD5如果已存在就直接把路徑復用不真正寫HDFS。這也就是“秒傳”的雛形。String md5 DigestUtils.md5Hex(new FileInputStream(tempFile));配合文件列表里的size字段可以做到“先查庫后傳文件”。對課程設計或面試來說這是個很能體現(xiàn)工程思維的加分項。能講清楚“MD5判重、文件內容只存一份、數(shù)據(jù)庫記錄引用關系”這三句話比多做兩個頁面更有價值。我自己的習慣是每次演示前先在Linux命令行里用hdfs dfs -du -h /看一眼空間占用再跑一次fsck確認沒有損壞塊最后才打開瀏覽器走上傳流程。這個習慣幫我避免了好幾次演示中途翻車也希望幫到你。本文還有配套的精品資源點擊獲取