如何把文件上傳服務器 上傳文件到服務器命令



文章插圖
如何把文件上傳服務器 上傳文件到服務器命令

文章插圖
簡介
上一篇的文章中,我們講到了如何從HTTP服務器中下載文件,和搭建下載文件服務器應該注意的問題,使用的GET方法 。本文將會討論一下常用的向服務器提交數據的POST方法和如何向服務器上傳文件 。
GET方法上傳數據
按照HTTP的規范,PUT一般是向服務器上傳數據,雖然不提倡,但是也可以使用GET向服務器端上傳數據 。
先看下GET客戶端的構建中需要注意的問題 。
GET請求實際上就是一個URI,URI后面帶有請求的參數,netty提供了一個QueryStringEncoder專門用來構建參數內容:
// HTTP請求QueryStringEncoder encoder = new QueryStringEncoder(get);// 添加請求參數encoder.addParam("method", "GET");encoder.addParam("name", "flydean");encoder.addParam("site", "www.flydean.com");URI uriGet = new URI(encoder.toString());有了請求URI,就可以創建HttpRequest了,當然這個HttpRequest中還需要有對應的HTTP head數據:
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());HttpHeaders headers = request.headers();headers.set(HttpHeaderNames.HOST, host);headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);headers.set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP + "," + HttpHeaderValues.DEFLATE);headers.set(HttpHeaderNames.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");headers.set(HttpHeaderNames.REFERER, uriSimple.toString());headers.set(HttpHeaderNames.USER_AGENT, "Netty Simple Http Client side");headers.set(HttpHeaderNames.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");headers.set(HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(new DefaultCookie("name", "flydean"),new DefaultCookie("site", "www.flydean.com")));我們知道HttpRequest中只有兩部分數據,分別是HttpVersion和HttpHeaders 。HttpVersion就是HTTP協議的版本號,HttpHeaders就是設置的header內容 。
對于GET請求來說,因為所有的內容都包含在URI中,所以不需要額外的HTTPContent,直接發送HttpRequest到服務器就可以了 。
channel.writeAndFlush(request);然后看下服務器端接收GET請求之后怎么進行處理 。
服務器端收到HttpObject對象的msg之后,需要將其轉換成HttpRequest對象,就可以通過protocolVersion(),uri()和headers()拿到相應的信息 。
對于URI中的參數,netty提供了QueryStringDecoder類可以方便的對URI中參數進行解析:
//解析URL中的參數QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());Map<String, List<String>> uriAttributes = decoderQuery.parameters();for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {for (String attrVal: attr.getValue()) {responseContent.append("URI: ").append(attr.getKey()).append('=').append(attrVal).append("\r\n");}}POST方法上傳數據
對于POST請求,它比GET請求多了一個HTTPContent,也就是說除了基本的HttpRequest數據之外,還需要一個PostBody 。
如果只是一個普通的POST,也就是POST內容都是key=value的形式,則比較簡單,如果POST中包含有文件,那么會比較復雜,需要用到ENCTYPE=”multipart/form-data” 。
netty提供了一個HttpPostRequestEncoder類,用于快速對request body進行編碼,先看下HttpPostRequestEncoder類的完整構造函數:
public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset,EncoderMode encoderMode)其中request就是要編碼的HttpRequest,multipart表示是否是”multipart/form-data”的格式,charset編碼方式,默認情況下是CharsetUtil.UTF_8 。encoderMode是編碼的模式,目前有三種編碼模式,分別是RFC1738,RFC3986和HTML5 。
默認情況下的編碼模式是RFC1738,這也是大多數form提交數據的編碼方式 。但是它并不適用于OAUTH,如果要使用OAUTH的話,則可以使用RFC3986 。HTML5禁用了multipart/form-data的混合模式 。
最后,我們講講HttpDataFactory 。factory主要用來創建InterfaceHttpData 。它有一個minSize參數,如果創建的HttpData大小大于minSize則會存放在磁盤中,否則直接在內存中創建 。
InterfaceHttpData有三種HttpData的類型,分別是Attribute, FileUpload和InternalAttribute 。
Attribute就是POST請求中傳入的屬性值 。FileUpload就是POST請求中傳入的文件,還有InternalAttribute是在encoder內部使用的,這里不過多討論 。
因此,根據傳入的minSize參數大小,Attribute和FileUpload可以被分成下面幾種:
MemoryAttribute, DiskAttribute or MixedAttributeMemoryFileUpload, DiskFileUpload or MixedFileUpload
在這一節我們先看一下在POST請求中并不上傳文件的處理方式,首先創建HTTP request和PostBody encoder:
// 構建HTTP requestHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); HttpPostRequestEncoder bodyRequestEncoder =new HttpPostRequestEncoder(factory, request, false);向request中添加headers:
// 添加headersfor (Entry<String, String> entry : headers) {request.headers().set(entry.getKey(), entry.getValue());}然后再向bodyRequestEncoder中添加form屬性:
// 添加form屬性bodyRequestEncoder.addBodyAttribute("method", "POST");bodyRequestEncoder.addBodyAttribute("name", "flydean");bodyRequestEncoder.addBodyAttribute("site", "www.flydean.com");bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);注意,上面我們向bodyRequestEncoder中添加了method,name和site這幾個屬性 。然后添加了一個FileUpload 。但是因為我們的編碼方式并不是”multipart/form-data”,所以這里傳遞的只是文件名,并不是整個文件 。
最后,我們要調用bodyRequestEncoder的finalizeRequest方法,返回最終要發送的request 。在finalizeRequest的過程中,還會根據傳輸數據的大小來設置transfer-encoding是否為chunked 。
如果傳輸的內容比較大,則需要分段進行傳輸,這時候需要設置transfer-encoding = chunked,否則不進行設置 。
最后發送請求:
// 發送請求channel.write(request);在server端,我們同樣需要構造一個HttpDataFactory,然后使用這個factory來構造一個HttpPostRequestDecoder,來對encoder出來的數據進行decode:
HttpDataFactory factory =new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);//POST請求decoder = new HttpPostRequestDecoder(factory, request);因為server端收到的消息根據發送消息的長度可以能是HttpContent,也可能是LastHttpContent 。如果是HttpContent,我們將解析的結果放到一個StringBuilder中緩存起來,等接收到LastHttpContent再一起發送出去即可 。
在收到HttpContent之后,我們調用decoder.offer方法,對HttpContent進行解碼:
decoder.offer(chunk);在decoder內部有兩個存儲HttpData數據的容器,分別是:
List<InterfaceHttpData> bodyListHttpData和Map<String, List<InterfaceHttpData>> bodyMapHttpDatadecoder.offer就是對chunk進行解析,然后將解析過后的數據填充到bodyListHttpData和bodyMapHttpData中 。
解析過后,就可以對解析過后的數據進行讀取了 。
可以通過decoder的hasNext和next方法對bodyListHttpData進行遍歷,從而獲取到對應的InterfaceHttpData 。
通過data.getHttpDataType()可以拿到InterfaceHttpData的數據類型,上面也講過了有Attribute和FileUpload兩種類型 。
POST方法上傳文件
如果要POST文件,客戶端在創建HttpPostRequestEncoder的時候傳入multipart=true即可:
HttpPostRequestEncoder bodyRequestEncoder =new HttpPostRequestEncoder(factory, request, true);然后分別調用setBodyHttpDatas和finalizeRequest方法,生成HttpRequest就可以向channel寫入了:
// 添加body http databodyRequestEncoder.setBodyHttpDatas(bodylist);// finalize request,判斷是否需要chunkrequest = bodyRequestEncoder.finalizeRequest();// 發送請求頭channel.write(request);要注意,如果是transfer-encoding = chunked,那么這個HttpRequest只是請求頭的信息,我們還需要手動將HttpContent寫入到channel中:
// 判斷bodyRequestEncoder是否是Chunked,發送請求內容if (bodyRequestEncoder.isChunked()) {channel.write(bodyRequestEncoder);}在server端,通過判斷InterfaceHttpData的getHttpDataType,如果是FileUpload類型,則說明拿到了上傳的文件,則可以通過下面的方法來讀取到文件的內容:
FileUpload fileUpload = (FileUpload) data;responseContent.append(fileUpload.getString(fileUpload.getCharset()));這樣我們就可以在服務器端拿到客戶端傳過來的文件了 。
總結
【如何把文件上傳服務器 上傳文件到服務器命令】HTTP的文件上傳需要考慮的問題比較多,大家有不明白的可以參考我的例子 ?;蛘吡粞越o我一起討論 。