Java中用Socket實(shí)現(xiàn)HTTP文件上傳實(shí)例
我想做過(guò)web開(kāi)發(fā)的程序員大部分都做過(guò)文件上傳的功能,大多數(shù)時(shí)候我們都是借助于commons-fileupload這樣的jar包實(shí)現(xiàn)的。下面我試著通過(guò)讀取Socket的輸入流來(lái)實(shí)現(xiàn)一個(gè)文件上傳的功能。
在做文件上傳之前我們需要先了解一下HTTP POST的附件上傳協(xié)議。HTTP附件上傳協(xié)議是RFC1876協(xié)議,RFC1876協(xié)議是在HTTP協(xié)議的基礎(chǔ)上為INPUT標(biāo)簽增加了file屬性,同時(shí)限定了Form的method必須為POST
,ENCTYPE
必須為multipart/form-data
。RFC1867協(xié)議對(duì)HTTP頭作了適當(dāng)?shù)刈兏?,content-type頭由以前的:content-type:application/x-www-form-urlencoded變?yōu)?code>content-type:multipart/form-data;+空格+boundary=字符串。RFC1867增加了文件上傳得功能,而上傳文件內(nèi)容自然也會(huì)被加入到HTTP的實(shí)體中?,F(xiàn)在因?yàn)榧扔蠬TTP一般的參數(shù)實(shí)體,又有上傳文件的實(shí)體,所以用boundary把每種實(shí)體進(jìn)行了分割。具體的看下圖:
接下來(lái)就開(kāi)始我們的代碼部分吧。
我在前面的文章中寫(xiě)過(guò)創(chuàng)建一個(gè)自己的Web服務(wù)器,現(xiàn)在我們的重點(diǎn)要放在對(duì)socket的輸入流的解析中。具體代碼如下:
public void parseRequest() { LineNumberReader br = new LineNumberReader(new InputStreamReader(inputStream)); StringBuffer sb = new StringBuffer(); String str = null; try { //讀取請(qǐng)求行 String requestLine = br.readLine(); if (!StringUtils.isEmpty(requestLine)) { sb.append(requestLine); String[] reqs = requestLine.split(" "); if (reqs != null && reqs.length > 0) { if ("GET".equals(reqs[0])) { method = "GET"; } else { method = "POST"; } } } //讀取請(qǐng)求頭 while ((str = br.readLine()) != null) { if ("".equals(str)) { break; } if (!StringUtils.isEmpty(str)) { if (str.indexOf(":") > 0) { String[] strs = str.split(":"); headers.put(strs[0].toLowerCase(), strs[1].trim()); } } sb.append(str).append("\n"); } //POST請(qǐng)求,Content-type為 multipart/form-data String contentType = null; if ("POST".equals(method) && ((contentType = headers.get("content-type")) != null && headers.get("content-type").startsWith("multipart/form-data"))) { //文件上傳的分割位 這里只處理單個(gè)文件的上傳 String boundary = contentType.substring(contentType.indexOf("boundary") + "boundary=".length()); //解析消息體 while ((str = br.readLine()) != null) { //解析結(jié)束的標(biāo)記 do { //讀取boundary中的內(nèi)容 //讀取Content-Disposition str = br.readLine(); //說(shuō)明是文件上傳 if (str.indexOf("Content-Disposition:") >= 0 && str.indexOf("filename") > 0) { str = str.substring("Content-Disposition:".length()); String[] strs = str.split(";"); String fileName = strs[strs.length - 1].replace("\"", "").split("=")[1]; System.out.println("fileName = " + fileName); //這一行是Content-Type br.readLine(); //這一行是換行 br.readLine(); //正式去讀文件的內(nèi)容 BufferedWriter bw = null; try { bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("G:\\LearnVideo\\fileLoad" + File.separator + fileName), "UTF-8")); while (true) { str = br.readLine(); if (str.startsWith("--" + boundary)) { break; } bw.write(str); bw.newLine(); } bw.flush(); } catch (Exception e) { } finally { if (bw != null) { bw.close(); } } } if (str.indexOf("Content-Disposition:") >= 0) { str = str.substring("Content-Disposition:".length()); String[] strs = str.split(";"); String name = strs[strs.length - 1].replace("\"", "").split("=")[1]; br.readLine(); StringBuilder stringBuilder = new StringBuilder(); while (true) { str = br.readLine(); if (str.startsWith("--" + boundary)) { break; } stringBuilder.append(str); } parameters.put(name, stringBuilder.toString()); } } while (("--" + boundary).equals(str)); //解析結(jié)束 if (str.equals("--" + boundary + "--")) { break; } } } //System.out.println(sb.toString()); //獲取URI uri = StringUtils.parserUri(sb.toString(), " "); int flag = -1; //說(shuō)明有參數(shù) if ((flag = uri.indexOf('?')) >= 0) { String oldUri = uri; uri = uri.substring(0,flag); String parameterPath = oldUri.substring(flag+1); String[] parameter = parameterPath.split("&"); if (parameter != null && parameter.length > 0) { for (int i = 0; i < parameter.length; i++) { String str1 = parameter[i]; if((flag = str1.indexOf('=')) >= 0){ String key = str1.substring(0,flag); String value = str1.substring(flag+1); parameters.put(key,value); }else{ parameters.put(str,null); } } } } } catch (IOException e) { e.printStackTrace(); } }
我們啟動(dòng)自己創(chuàng)建的Web服務(wù)器,然后在瀏覽器中輸入:http://localhost:8004/static/uploadPage.html,頁(yè)面如下:
選擇我們要上次的文件,然后點(diǎn)擊上傳按鈕,我們會(huì)發(fā)現(xiàn)我們的功能已經(jīng)被上傳到G:\LearnVideo\fileLoad這個(gè)目錄下了。示例如下:
完整的代碼請(qǐng)從這里下載:FullStackTraining_jb51.rar
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java 基于TCP Socket 實(shí)現(xiàn)文件上傳
- Socket+JDBC+IO實(shí)現(xiàn)Java文件上傳下載器DEMO詳解
- Java Socket+mysql實(shí)現(xiàn)簡(jiǎn)易文件上傳器的代碼
- JavaWeb實(shí)現(xiàn)文件上傳下載功能實(shí)例解析
- JAVA中使用FTPClient實(shí)現(xiàn)文件上傳下載實(shí)例代碼
- java實(shí)現(xiàn)FTP文件上傳與文件下載
- JavaWeb實(shí)現(xiàn)文件上傳下載功能實(shí)例詳解
- java web圖片上傳和文件上傳實(shí)例
- Java實(shí)現(xiàn)文件上傳至服務(wù)器的方法
- java使用Socket實(shí)現(xiàn)文件上傳功能
相關(guān)文章
SpringBoot基于Swagger2構(gòu)建API文檔過(guò)程解析
這篇文章主要介紹了SpringBoot基于Swagger2構(gòu)建API文檔過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)與應(yīng)用詳解
定時(shí)任務(wù)在許多應(yīng)用場(chǎng)景中是必不可少的,特別是在自動(dòng)化任務(wù)執(zhí)行、定期數(shù)據(jù)處理等方面,定時(shí)任務(wù)能極大地提高系統(tǒng)的效率,然而,隨著業(yè)務(wù)需求的變化,定時(shí)任務(wù)的執(zhí)行頻率或時(shí)間點(diǎn)可能需要?jiǎng)討B(tài)調(diào)整,所以本文給大家介紹了SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)與應(yīng)用2024-08-08Java?Jar包項(xiàng)目?jī)?nèi)存設(shè)置方法舉例
這篇文章主要給大家介紹了關(guān)于Java?Jar包項(xiàng)目?jī)?nèi)存設(shè)置方法的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-01-01