在,超過10億人在使用移動智能電話,有人預測,這一數字在2015年也將會翻一倍。在這種愈發明顯的趨勢下,開發者面臨的挑戰就是怎么樣利用最合理的工作量和工作成本,在最短的時間內,解決面臨的問題。用戶使用HTML5 Builder,可以使用網絡標準,只需要單一的代碼基就可以同時構建web和移動應用,并通過桌面和移動web瀏覽器來交付應用。除此之外,使用者也是可以通過蘋果,安卓,黑莓等的移動應用商店來交付并可直接在設備上運行的原生應用。
在這種情況下,HTML5 Builder就是用戶web和移動開發者的力量倍增器,使得用戶能夠在更多的時間內,用更少的工作量就能夠部署到更多的平臺。
以下為HTML5 Builder 的特點
第一、 只需一次創建用戶應用,并將其部署到多個web和移動平臺即可;
為每個移動平臺和和規格創建應用,使用HTML5 Builder,用戶可使用單一的HTML5、CSS3、JavaScript和PHP代碼基開發使用者的應用一次,只需一次點擊就可部署到多個移動操作系統、設備和Web瀏覽器。
第二 、 使用單一可視化框架增進開發速度;
使用HTML5 Builder,用戶不必需要學習新的編程語言或撰寫代碼,也不用知道怎樣使用jQuery或移動JavaScript庫。HTML5 Builder內置了幾百個可重用、可拖拽的組件,用戶可按需使用或對其加以定制來幫助更快地構建應用。
第三 、 通過協作式的工作流,讓設計師與開發者能緊密協同工作;
現代以及動態的用戶體驗使得廣泛用戶的應用能與時下流行的CSS3樣式和動畫等形成集成。
第四、 創建企業或ISV web和移動應用;
無論是企業還是ISV,都可以使用HTML5 Builder創建從簡單到復雜的web和移動應用。
下面看看HTML5 Builder的新增特性又表現在哪些方面
第一、 直觀優化的用戶界面
流暢的開發者用戶界面;
強大的可視化應用開發;
幾百個可拖拽組件;
基于上下文的工作流。
第二、 通過可視化HTML5框架實現快速開發
可視化HTML5應用開發;
使用JavaScript編寫客戶端功能;
可選集成的服務器端PHP代碼;
HTML5可視化組件,包括畫布和多媒體。
第三、 高性能HTML5本地存儲
用于開發移動web應用和網站的API;
利用瀏覽器的存儲能力,取代對第三數據庫的依賴;
快速的網絡數據訪問性能,甚至即使在瀏覽器關閉后。
第四、 動態實時瀏覽
撰寫HTML和JS代碼或者使用現有的模板;
實時查看代碼修改,并可在進入生產環境前獲得用戶項目的實時預覽;
設計現有的用戶界面組件,并實時預覽。
第五、 強大的數據庫支持
完整支持主流數據庫管理系統;
連接到MySQL,InterBase,FireBird,DB2以及更多。
持word上傳的富文本編輯器,web編輯器,HTML編輯器,前端編輯器。編輯器網上搜了一下,比較多,
前端用了VUE2,VUE3,React,HTML5,也是一個新項目,為了方便用的這些框架,實際上這個是無所謂,功能的實現和前端這些框架沒什么關系。
后端用了PHP,JSP,ASP,ASP.NET,SpringBoot,功能實現和后端用的什么開發語言無關,后端只提供一個文件上傳的接口,HTTP form協議,圖片上傳時會調這個接口。
編輯器是ckeditor5,為ckeditor編輯器增加粘貼Word圖片的功能,支持快捷鍵操作(Ctrl+V),支持多種系統:Windows,macOS,Linux,信創國產化環境,中標麒麟,銀河麒麟,統信,龍芯。
支持word粘貼,word內容粘貼,word圖文粘貼,word圖片粘貼,粘貼后圖片能夠自動上傳到服務器中,然后將圖片和內容HTML添加到編輯器中,上傳接口地址能夠自定義
示例項目:https://gitee.com/xproer/wordpaster-vue-ckeditor5
1.為ckeditor增加插件按鈕
1.下載ckeditor5源碼,注意:不是在npm中直接安裝ckeditor5,在npm中直接安裝的ckeditor5是已經打包好的編輯器,我們無法為其添加插件,必須下載源碼添加好后再手動打包。
git clone -b stable https://github.com/ckeditor/ckeditor5-build-classic.git
cd ckeditor5-build-classic
2.添加插件文件
將plugin下的插件復制到ckeditor5的目錄中
導入插件
在ckeditor.js中導入插件
3.添加插件
添加到工具欄
# 構建,生成ckeditor.js,生成的ckeditor.js在build目錄下
yarn build
執行命令后生成ckeditor.js
將打包好的ckeditor.js復制到項目中
2.復制layer,wordpaster目錄,安裝jquery
#通過命令安裝jquery
npm install jquery
3.在組件中導入樣式
代碼:
import {WordPaster,WordPasterManager} from ‘../../static/WordPaster/js/w’
import ‘../../static/WordPaster/js/w.css’
import ‘../../static/layer-v3.1.1/layer/layer’
import ‘../../static/layer-v3.1.1/layer/theme/default/layer.css’
4.初始化wordpaster組件
配置posturl,
代碼:
//初始化
WordPaster.getInstance({
PostUrl:”http://localhost:8891/upload.aspx”,
ImageUrl:”http://localhost:8891{url}”
}).Load();
整合效果:
上一篇的文章中,我們講到了如何從HTTP服務器中下載文件,和搭建下載文件服務器應該注意的問題,使用的GET方法。本文將會討論一下常用的向服務器提交數據的POST方法和如何向服務器上傳文件。
按照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請求,它比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 MixedAttribute
MemoryFileUpload, DiskFileUpload or MixedFileUpload
在這一節我們先看一下在POST請求中并不上傳文件的處理方式,首先創建HTTP request和PostBody encoder:
// 構建HTTP request
HttpRequest request=new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
HttpPostRequestEncoder bodyRequestEncoder= new HttpPostRequestEncoder(factory, request, false);
向request中添加headers:
// 添加headers
for (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>> bodyMapHttpData
decoder.offer就是對chunk進行解析,然后將解析過后的數據填充到bodyListHttpData和bodyMapHttpData中。
解析過后,就可以對解析過后的數據進行讀取了。
可以通過decoder的hasNext和next方法對bodyListHttpData進行遍歷,從而獲取到對應的InterfaceHttpData。
通過data.getHttpDataType()可以拿到InterfaceHttpData的數據類型,上面也講過了有Attribute和FileUpload兩種類型。
如果要POST文件,客戶端在創建HttpPostRequestEncoder的時候傳入multipart=true即可:
HttpPostRequestEncoder bodyRequestEncoder= new HttpPostRequestEncoder(factory, request, true);
然后分別調用setBodyHttpDatas和finalizeRequest方法,生成HttpRequest就可以向channel寫入了:
// 添加body http data
bodyRequestEncoder.setBodyHttpDatas(bodylist);
// finalize request,判斷是否需要chunk
request=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的文件上傳需要考慮的問題比較多,大家有不明白的可以參考我的例子。或者留言給我一起討論。
本文的例子可以參考:learn-netty4
本文已收錄于 http://www.flydean.com/21-netty-http-fileupload/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。