本系列文章旨在記錄和總結自己在Java Web開發之路上的知識點、經驗、問題和思考,希望能幫助更多(Java)碼農和想成為(Java)碼農的人。
上篇文章介紹了JSP的核心原理,本篇文章準備介紹JSP的初步使用。
大家一定要自己動手編寫代碼,運行驗證,只有這樣才能理解的更加透徹,印象也更加深刻。不知道大家有沒有這樣一種體會,就是明明自己看了不少技術相關的書籍,但是每到用的時候就忘記了相關技術如何使用,還是需要不斷的上網搜索。
這還是說明我們動手實踐比較少。在我看來,任何一門科學、技術、學問、專業都離不開以下四個環節:
任何一門科學、技術、學問、專業都不是一日之功,需要長期的堅持這四個環節,所以必須從小就灌輸,養成學、做、思、記的習慣。這四個環節是互相影響、互相交叉、互相促進、需要同時進行,不可偏廢的。正所謂“紙上得來終覺淺,絕知此事要躬行”、“學而不思則罔,思而不學則殆”、“好記性不如爛筆頭”等等。
雖說這四個環節不可偏廢,但各門科學、技術、學問、專業還是有所側重的,比如科學這個詞感覺就偏理論學習和思考多一些,動手實踐少一些;而技術這個詞就相反。再比如工科偏動手實踐多一些,理科偏理論研究多一些等等。
不好意思,又跑題。說那么多無非就是想說我們需要多敲敲代碼。軟件/編程這一行業被劃分到工科,那就說明我們更要多動手實踐。話說這應該是實踐成本最低的工科行業了,只要一臺個人電腦即可,還有各種開源的軟件可用,要是建筑、土木、工業制造、各種勘探、化工、電力等等,要么需要去實地、要么需要建個實驗室、要么需要買個機床。。。。。。
好了,不說廢話了。我們仍然以前面開發的租房網應用(可以參考這篇文章和這篇文章)為基礎,使用JSP技術來改造,至少需要達到的一個目標是讓代碼看起來更加清爽。
工程結構:
靜態資源 - 登錄頁面login.html:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>租房網 - 登錄</title> </head> <body> <form action="login.servlet" method="post"> <label for="user_name">用戶名:</label><input type="text" id="user_name" name="userName" /> <label for="password">密碼:</label><input type="password" id="password" name="password" /> <input type="submit" value="登錄" /> </form> </body> </html>
房源實體類 - House.java:
package houserenter.entity; public class House { private String id; private String name; private String detail; public House(String id, String name, String detail) { super(); this.id = id; this.name = name; this.detail = detail; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } @Override public String toString() { return "House [id=" + id + ", name=" + name + ", detail=" + detail + "]"; } }
處理登錄請求的Servlet - LoginServlet.java:
package houserenter.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/login.servlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userName = request.getParameter("userName"); String password = request.getParameter("password"); //這里需要驗證用戶是否已經注冊,省略 System.out.println("userName: " + userName + ", password: " + password); //用戶登錄成功,重定向到房源列表頁面 response.sendRedirect("house.html?userName=" + userName); } }
處理房源相關請求(房源查找、房源詳情、房源編輯)的Servlet - HouseServlet.java:
package houserenter.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import houserenter.entity.House; @WebServlet("/house.html") public class HouseServlet extends HttpServlet { private static final long serialVersionUID = 1L; private List<House> mockHouses; @Override public void init() { mockHouses = new ArrayList<House>(); mockHouses.add(new House("1", "金科嘉苑3-2-1201", "詳細信息")); mockHouses.add(new House("2", "萬科橙9-1-501", "詳細信息")); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userName = request.getParameter("userName"); PrintWriter writer = response.getWriter(); writer.println("<!DOCTYPE html>"); writer.println("<html>"); writer.println("<head>"); writer.println("<meta charset=\"UTF-8\">"); writer.println("<title>租房網</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("<h1>你好,"+userName+"!歡迎來到租房網! <a href=\"login.html\">退出</a></h1>"); writer.println("<br><br>"); String houseId = request.getParameter("houseId"); String editHouse = request.getParameter("editHouse"); if (houseId == null || houseId.isEmpty()) { //查找該用戶感興趣的房源,這里省略 System.out.println("userName: " + userName + " access house.html!"); writer.println("<h6>共找到你感興趣的房源 "+mockHouses.size()+" 條</h6>"); writer.println("<ul>"); for (House house : mockHouses) { writer.println("<li><h2><a href=\"house.html?userName="+userName+"&houseId="+house.getId()+"\">"+house.getName()+"</a></h2></li>"); } writer.println("</ul>"); } else if (editHouse == null) { //根據houseId查找該房源的詳細信息 System.out.println("userName: " + userName + " access house.html for house detail!"); House target = null; for (House house : mockHouses) { if (houseId.equals(house.getId())) { target = house; break; } } writer.println("<h2>"+target.getName()+"<a href=\"house.html?userName="+userName+"&houseId="+houseId+"&editHouse=true\">編輯</a></h2>"); writer.println("<h3>"+target.getDetail()+"</h3>"); writer.println("<h4><a href=\"house.html?userName="+userName+"\">回到列表</a></h4>"); } else { //存在editHouse參數,返回指定房源的編輯頁面 System.out.println("userName: " + userName + " access house.html to edit house!"); House target = null; for (House house : mockHouses) { if (houseId.equals(house.getId())) { target = house; break; } } //writer.println("<form action=\"house.html?userName="+userName+"&houseId="+houseId+"\" method=\"post\">"); writer.println("<form action=\"house.html\" method=\"post\">"); writer.println("<input type=\"hidden\" name=\"userName\" value=\""+userName+"\"/>"); writer.println("<input type=\"hidden\" name=\"houseId\" value=\""+houseId+"\"/>"); writer.println("<label for=\"house_name\">房源名字:</label><input type=\"text\" id=\"house_name\" name=\"houseName\" value=\""+target.getName()+"\" />"); writer.println("<label for=\"house_detail\">房源詳細信息:</label><input type=\"text\" id=\"house_detail\" name=\"houseDetail\" value=\""+target.getDetail()+"\" />"); writer.println("<input type=\"submit\" value=\"提交\" />"); writer.println("</form>"); } writer.println("</body>"); writer.println("</html>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userName = request.getParameter("userName"); String houseId = request.getParameter("houseId"); //獲取提交的房源信息,并保存 System.out.println("userName: " + userName + " access house.html to save house detail!"); String houseName = request.getParameter("houseName"); String houseDetail = request.getParameter("houseDetail"); for (House house : mockHouses) { if (houseId.equals(house.getId())) { house.setName(houseName); house.setDetail(houseDetail); break; } } response.sendRedirect("house.html?userName="+userName+"&houseId="+houseId); } }
設置請求響應編碼、登錄驗證的Filter - MyFirstFilter.java:
package houserenter.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebFilter("/house.html") public class MyFirstFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; String userName = httpServletRequest.getParameter("userName"); if (userName == null || userName.isEmpty()) { System.out.println("invalid user!"); httpServletResponse.sendRedirect("login.html"); } else { chain.doFilter(request, response); } } }
登錄頁面和LoginServlet目前還算簡單,暫時可以不用改造。
HouseServlet的代碼量稍微有點多(也不過一百多行),且夾雜著輸出HTML內容,看著比較亂。從功能上來說雖然都與房源有關,但實際上動態生成的呈現給瀏覽器的是三個頁面(房源列表、房源詳情、房源編輯),所以可以考慮用三個JSP頁面代替,咱就命名為:
另外,因為涉及到一個房源編輯的表單,所以需要一個Servlet來處理表單的提交,就叫HouseFormServlet吧。
還有一個要提的是HouseServlet初始化了一些模擬數據,那這部分應該放在哪呢?因為一個JSP頁面就相當于一個Servlet,所以原來的HouseServlet現在要改造成三個JSP頁面,感覺模擬數據放在哪個JSP頁面都不太合適,因為三個JSP頁面都要訪問同一份模擬數據才行。
于是問題變為:如何在多個JSP頁面之間共享訪問同一份數據?我先想到的是能不能在一個Filter中初始化模擬數據,然后讓它攔截到這三個JSP頁面的請求,然后在請求中使用setAttribute()方法將模擬數據掛上去,最后在JSP頁面中使用請求的getAttribute()方法取出來。
好了,既然思路有了,那我們就大刀闊斧的干吧。
不過我們可以把之前的代碼都保留著,另外編寫新添加的JSP頁面和Filter即可,當然也可以把之前的都刪掉。
這次我們先編寫我們的Filter。
在Filter中初始化模擬數據跟之前在HouseServlet中是類似的。Filter也有一個相同的生命周期方法init()(另一個相同的是destroy()方法),大家可以直接看看Filter的源碼(如何查看源碼可以參考這篇文章)。
不過Filter的這個init()方法是Servlet容器初始化Filter的時候就調用,與Servlet的init()方法在第一個請求到來時才調用是不同的。
Filter的doFilter()方法還是可以跟之前的Filter一樣設置請求響應的編碼以及進行登錄驗證,但最主要的是調用請求的setAttribute()方法把模擬數據掛載到該請求中。
在Eclipse中創建Filter還是可以使用New工具(可以參考這篇文章中的新建Java類部分),大家應該很容易知道該怎么填寫相關信息,比如Filter的類名、初始化參數、映射(即攔截何種請求)。當然,你也可以新建空白的文件手動敲寫所有代碼。
package houserenter.filter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import houserenter.entity.House; @WebFilter( urlPatterns = { "/houses.jsp", "/house-details.jsp", "/house-form.jsp", "house-form.servlet" }) public class MySecondFilter implements Filter { private List<House> mockHouses; @Override public void init(FilterConfig filterConfig) { System.out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); mockHouses = new ArrayList<House>(); mockHouses.add(new House("1", "金科嘉苑3-2-1201", "詳細信息")); mockHouses.add(new House("2", "萬科橙9-1-501", "詳細信息")); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("BBBBBBBBBBBBBBBBBBBBBBBBBBBB"); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; Object obj = httpServletRequest.getAttribute("mockHouses"); if (obj == null) { httpServletRequest.setAttribute("mockHouses", mockHouses); } String userName = httpServletRequest.getParameter("userName"); if (userName == null || userName.isEmpty()) { System.out.println("invalid user!"); httpServletResponse.sendRedirect("login.html"); } else { chain.doFilter(request, response); } } }
上面的代碼是我用New工具生成之后,把無用的注釋、成員方法刪掉之后,再加上初始化模擬數據的邏輯、doFilter的邏輯。
因為Filter接口的init()方法和destroy()方法是default的(JDK8才支持),因此可以不必實現。
注意,此Filter攔截的有哪些資源的訪問請求!
doFilter的邏輯跟之前的相比,多了一個把mockHouses掛載到請求上去的部分。經過測試,不用強制轉換成HttpServletRequest也是可以的。
對了,我在init()方法和doFilter()方法中都加了打印日志的部分,便于調試。
現在開始編寫我們的JSP頁面,第一個是房源列表。
編寫房源列表頁面,實際上就是編寫Servlet,只不過它們的HTML和Java內容是反著來的。所以你可以參照原來的HouseServlet中是怎樣輸出HTML的,你就可以把它們拷貝過來,然后把writer.println("")這種去掉,把HTML標簽拿出來,而需要Java代碼的地方就用JSP語法中的<%和%>括起來即可。
即編寫JSP頁面時,你就可以想象著這是在編寫Servlet的service()方法(除去JSP的聲明,即<%!和%>括起來的,和page指令中的import部分以外),只不過HTML內容直接寫,而Java代碼需要<%(或<%=)和%>括起來。
但實際上,我們仍然可以使用New工具,從而生成通用的JSP模板頁面(工具中可以選擇是HTML 5、HTML 4.01、XHTML等等,我選的是HTML5)。
我們把這幾個JSP頁面都直接放在WebContent節點下,這樣跟MySecondFilter的映射配置是一致的。
這里有一個小訣竅,就是寫一部分代碼驗證一部分代碼,不必整個頁面編寫完畢才驗證,當然我們這幾個頁面都比較簡單,也可以全部編寫完畢再驗證。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.List" %> <%@ page import="houserenter.entity.House" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>租房網</title> </head> <body> <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1> <br><br> <% List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); System.out.println(mockHouses); %> <h6>共找到你感興趣的房源 <%=mockHouses.size() %> 條</h6> <ul> <%for (House house : mockHouses) { System.out.println(house); %> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li> <%} %> </ul> </body> </html>
需要提一下:
我們可以一邊開發JSP頁面一邊進行驗證了,Eclispe中啟動Tomcat之后也不用關閉,它會自動檢測JSP頁面和Java代碼的變化,進行重新編譯和發布,不過有時候修改代碼后還是重新發布應用比較踏實。
先別忘了修改我們的LoginServlet重定向的地址:
response.sendRedirect("houses.jsp?userName=" + userName);
OK,從瀏覽器中訪問租房網的登錄頁面login.html:
隨便輸入用戶名和密碼,點擊登錄:
耶,完全沒有問題!繼續編寫其他兩個JSP頁面。
有了前面的分析,相信大家都能夠理解一般的JSP代碼了,那就直接上代碼吧。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.List" %> <%@ page import="houserenter.entity.House" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>租房網</title> </head> <body> <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1> <br><br> <% List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); String houseId = request.getParameter("houseId"); House target = null; for (House house : mockHouses) { if (houseId.equals(house.getId())) { target = house; break; } } %> <h2><%=target.getName() %><a href="house-form.jsp?userName=${param.userName }&houseId=<%=target.getId() %>">編輯</a></h2> <h3><%=target.getDetail() %></h3> <h4><a href="houses.jsp?userName=${param.userName }">回到列表</a></h4> </body> </html>
還是主要用了JSP腳本、JSP表達式和EL表達式三個技術。
不過,JSP腳本看著似乎有點長。
還是直接上代碼。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.List" %> <%@ page import="houserenter.entity.House" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>租房網</title> </head> <body> <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1> <br><br> <% List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); String houseId = request.getParameter("houseId"); House target = null; for (House house : mockHouses) { if (houseId.equals(house.getId())) { target = house; break; } } %> <form action="house-form.servlet" method="post"> <input type="hidden" name="userName" value="${param.userName}"/> <input type="hidden" name="houseId" value="<%=target.getId() %>"/> <label for="house_name">房源名字:</label><input type="text" id="house_name" name="houseName" value="<%=target.getName() %>" /> <label for="house_detail">房源詳細信息:</label><input type="text" id="house_detail" name="houseDetail" value="<%=target.getDetail() %>" /> <input type="submit" value="提交" /> </form> </body> </html>
注意,此時表單的提交路徑是:house-form.servlet
所以,后面編寫HouseFormServlet時,配置其URL映射模式要與此一致。
無非就是提取表單提交的數據,然后保存到我們的模擬數據中,最后重定向回該房源的詳情頁面。
package houserenter.servlet; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import houserenter.entity.House; @WebServlet("/house-form.servlet") public class HouseFormServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); String houseId = request.getParameter("houseId"); House target = null; for (House house : mockHouses) { if (houseId.equals(house.getId())) { target = house; break; } } String houseName = request.getParameter("houseName"); target.setName(houseName); String houseDetail = request.getParameter("houseDetail"); target.setDetail(houseDetail); String userName = request.getParameter("userName"); response.sendRedirect("house-details.jsp?userName=" + userName + "&houseId=" + houseId); } }
至此,全部JSP頁面和Servlet代碼已經編寫完畢,大家可以自行驗證,應該沒有問題。
雖然已經完成了全部工作,運行也沒有問題,但是很明顯,三個JSP頁面中開頭的很大一部分都是重復的,這時候JSP中的include指令就排上用場了。
我們把這部分重復的提取出來,形成一個獨立的JSP文件:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.List" %> <%@ page import="houserenter.entity.House" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>租房網</title> </head> <body> <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1> <br><br>
然后改造三個JSP頁面,比如房源列表頁面houses.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="include.jsp"%> <% List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); System.out.println(mockHouses); %> <h6>共找到你感興趣的房源 <%=mockHouses.size() %> 條</h6> <ul> <%for (House house : mockHouses) { System.out.println(house); %> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li> <%} %> </ul> </body> </html>
直接使用include指令:
<%@ include file="include.jsp"%>
但是,要注意,房源列表頁面houses.jsp中仍然需要下面的page指令,否則會有中文亂碼:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
實際上,include指令也是由Servlet容器在將JSP頁面生成Servlet代碼的過程中解析,并用包含的JSP頁面的內容替換該指令,完全是解析階段發生的故事。
最后,我們的租房網的工程結構變成:
雖然我們的租房網還不夠完美,但總算比之前純粹使用Servlet的時候干凈清爽多了,頁面結構也比較清晰合理。
當然,還有很多需要改進的地方,讀者朋友們自己可以思考思考。
TML5是移動互聯網的未來嗎?
自2010年喬布斯公開支持并在iOS禁止Flash后,在多數開發者心中,這開始變成一個肯定的答案。
2010年到2011年,HTML5概念被熱炒,受到追捧,甚至不少人預言HTML5應用將會替代原生app。但或許是當初被捧得太高,而不論是生態環境還是技術支持都遠遠算不上成熟,HTML5游戲在短暫熱捧之后遭遇諸多問題,隨后開始陷入冰谷,當初使用HTML5開發游戲的團隊紛紛轉型——HTML5或許將是未來,但現在談論還為時過早。
在被遺忘一段時間之后,現在,一些專注于游戲領域的HTML5引擎服務提供商正在重整旗鼓(詳見今日推送的第二篇文章),這讓我們不由再次回頭審視HTML5游戲走過的整個路程,并開始思考一個問題:在HTML5那條通往“未來”的遙遠道路上,它正站在一個什么樣的位置上?
■過去
2010年4月,蘋果公司宣布禁止FlashPlayer登陸iOS系統,鼓勵開發者使用HTML5技術,這一舉動引起了軒然大波,喬布斯甚至為此撰寫了一篇長文《關于Flash的幾點思考》進行回應。當年十月,Zynga收購了HTML5游戲引擎開發商Dextrose,并在隨后發布了第一款HTML5游戲《MafiaWarsAtlanticCity》。
從2011年開始,HTML5的概念開始火爆,在這一年,諸多大廠紛紛出擊這一領域進行布局,MOTO投資了HTML5游戲公司Moblyng,迪士尼收購了HTML5游戲引擎公司RocketPack。
Facebook社交游戲開發商Wooga也在當年宣布進軍HTML5和iOS游戲領域。
在同一年,Unity確認支持HTML5,Facebook收購HTML5技術團隊Strobe,將自身在移動端的發力重心放在HTML5上。同年,手游大廠EA、Popcap、Gameloft也相繼發布了自己的HTML5游戲。
在2011年底,還有一件重要的事情,Adobe宣布停止在Android系統更新FlashPlayer,并推薦開發者使用HTML5技術開發移動Web應用。
就在看起來形勢一片大好HTML5概念火熱的時候,哀歌從2012年開始唱響。
在2012年開年之際,此前MOTO投資的HTML5游戲開發商Moblyng倒閉是第一個音符,隨后在六月,Wooga宣布停止開發HTML5游戲,他們曾經推出一款HTML5游戲《MagicLand:Island》,但是玩家玩這個游戲的總次數只有130萬次,留存率僅5%,相比之下,他們在iOS平臺推出的《DiamondDash》則獲得了1800萬次的下載。
為這支哀歌譜下強音的是Facebook的失敗。在2012年9月,扎克伯格在接受采訪時表示:“Facebook曾經錯誤地將賭注押在了HTML5上,這是我們最大的戰略錯誤,致使我們錯失了移動市場的發展良機。”
在那之后,曾經被捧上高位打上未來標簽的HTML5技術在移動端狠狠摔落,HTML5開始淡出視野。
總結下來,HTML5在移動游戲領域所遭遇的困境,主要是由于四個原因:
1、技術不成熟,開發生態不完整
2、沒有合適的載體,瀏覽器渲染性能低下
3、沒有成熟的生態環境(渠道,運營商)
4、受制于網絡環境
這些原因最終造成了游戲功能和表現受限,體驗大打折扣。
在2013年底,下一代JavaScript標準規范ES6草案鎖定并正式發布。
■現在
目前HTML5的框架和庫都普遍偏于Web應用的制作,專注于游戲的偏少,比較流行的HTML5框架包括CreateJS,JQuery,AngularJS和Node.js,專注于HTML5游戲的引擎有Impact、Phaser、Pixi、Createjs、EaselJSPhaser、Turbulenz、GameClosure、Coco2d-HTML5和Egret等。
現在主流網站幾乎全部支持HTML5標準,幾乎所有流行的網站都采用了HTML5技術。但是在移動設備上,還尚未出現非常成熟的HTML5應用或游戲,這一市場還在剛剛起步。游戲方面,此前有一二三國、修仙三國、三國喵喵傳等游戲,而目前,墨麟、游戲谷、光年互動等開發商正在開發HTML5游戲。
正在發生的另外一些事情:
1.各瀏覽器對HTML5標準化的支持正在慢慢趨于一致
2.硬件的變革正在不斷推動采用HTML5技術制作的復雜應用和游戲的用戶體驗的快速提升
3.Web游戲類型在3G/4G網絡下的數據發送接收速度正在變得相對高效,但目前國內總體網絡質量仍不樂觀
4.逐漸涌現出的HTML5游戲引擎和制作工具,開始降低H5游戲制作成本,并提高游戲開發效率
5.混生應用出現,上層使用h5開發,底層使用c++渲染,性能得到很大提升,這種方式是當前階段的主流
6.一些巨頭正在嘗試引領這個市場,騰訊的手機QQ空間的安卓版應用,在前段時間將“玩吧”菜單放置在了底部菜單欄的一級入口;在玩吧中,現在已經上線了不少HTML5游戲,不僅有休閑游戲,也有一些卡牌類的中重度游戲,同時在安裝QQ空間首次登錄時,會自動進入一個名為“讓童年飛”的HTML5休閑游戲,騰訊正在嘗試引導用戶嘗試這些HTML5游戲,不過總體來說,內容還處于匱乏階段,玩吧目前僅提供13款游戲;此外還有百度輕應用等。
看起來一切正在往前有序推進,不過站在游戲開發者角度來說又是什么看法?
在一些開發者看來,HTML5游戲開發快、易調試、跨平臺、推廣成本更低的特點,或許會是其優勢所在,但是問題的核心在于如果不能在游戲體驗上給予玩家更多好處,那么就沒有太多的理由去看好,基于這點才會有市場,別的都是業內臆想。
而對于游戲玩家來說,內容才是永遠的核心。
■未來
HTML5的成熟條件是什么?
sp之大文件分段上傳、斷點續傳,jsp之大文件分段上傳,jsp之大文件分塊上傳,jsp之大文件分片上傳,jsp之大文件切片上傳,jsp之大文件加密上傳,jsp之大文件斷點續傳,jsp之大文件批量上傳,jsp之文件夾上傳,jsp之文件夾批量上傳,
文件比較大,有20G左右,用戶希望能夠在網頁里面直接上傳,一期的時候我們是直接用的HTML5的API,也就是chrome提供的API來做的,但是上線后用戶反饋不是特別的好用,用戶那邊有些電腦用的是WIN7+IE9,chrome的API在ie9里面不支持。但是用戶系統是支持的,這就把人整的有點不會了。
前端用了JSP,VUE2,VUE3,后端用了JSP,SpringBoot,IDE用了Eclipse,MyEclipse,因為新項目和老項目都用了兩種IDE。
用戶要求能夠在網頁上面上傳文件夾,文件夾里面大約有1萬多個文件,有大有小,大的有1G~10G,小的有幾MB,文件夾上傳的時候需要保存層級結構,同時能夠將層級結構信息保存到數據庫中,同時還需要支持文件夾下載,下載下來的文件夾要和上傳的文件夾層級結構一模一樣。
要求支持斷點續傳,支持進度信息離線存儲,用戶可能傳一半沒有傳完,下班了,明天上班后繼續上傳,電腦晚上到點需要關機,支持加密傳輸,支持國密加密算法SM4,要求支持下載,支持非打包方式下載,瀏覽器要求支持包含IE在內的所有瀏覽器,
斷點續傳,就是在文件上傳的過程中發生了中斷,人為因素(暫停)或者不可抗力(斷網或者網絡差)導致了文件上傳到一半失敗了。然后在環境恢復的時候,重新上傳該文件,而不至于是從新開始上傳的。
斷點續傳的功能是基于分塊上傳來實現的,把一個大文件分成很多個小塊,服務端能夠把每個上傳成功的分塊都落地下來,客戶端在上傳文件開始時調用接口快速驗證,條件選擇跳過某個分塊。
實現原理,就是在每個文件上傳前,就獲取到文件MD5取值,在上傳文件前調用接口,如果獲取的文件狀態是未完成,則返回所有的還沒上傳的分塊的編號,然后前端進行條件篩算出哪些沒上傳的分塊,然后進行上傳。
當接收到文件塊后就可以直接寫入到服務器的文件中。
導入項目:
導入到Eclipse:http://www.ncmem.com/doc/view.aspx?id=9da9c7c2b91b40b7b09768eeb282e647
導入到IDEA:http://www.ncmem.com/doc/view.aspx?id=9fee385dfc0742448b56679420f22162
springboot統一配置:http://www.ncmem.com/doc/view.aspx?id=7768eec9284b48e3abe08f032f554ea2
下載示例:
https://gitee.com/xproer/up6-jsp-eclipse/tree/6.5.40/
工程
NOSQL
NOSQL示例不需要任何配置,可以直接訪問測試
創建數據表
選擇對應的數據表腳本,這里以SQL為例
修改數據庫連接信息
訪問頁面進行測試
文件存儲路徑
up6/upload/年/月/日/guid/filename
相關問題:
1.javax.servlet.http.HttpServlet錯誤
2.項目無法發布到tomcat
3.md5計算完畢后卡住
4.服務器找不到config.json文件
相關參考:
文件保存位置
源碼工程文檔:https://drive.weixin.qq.com/s?k=ACoAYgezAAw1dWofra
源碼報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwoiul8gl
OEM版報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwuzp4W0a
產品源代碼:https://drive.weixin.qq.com/s?k=ACoAYgezAAwbdKCskc
授權生成器:https://drive.weixin.qq.com/s?k=ACoAYgezAAwTIcFph1
*請認真填寫需求信息,我們會在24小時內與您取得聯系。