JavaWeb——JSP開發2
阿新 • • 發佈:2018-01-30
web-inf col ssa throw length 填寫信息 keys ide 接收
使用JSP+Servlet實現文件的上傳和下載功能
1、文件模型
首先是文件本身,這裏創建一個類記錄文件的名字和內容:
public class Attachment { private String name; private byte[] contents; public Attachment() { } public String getName() { return this.name; } public void setName(String name) { this.name = name; }public byte[] getContents() { return this.contents; } public void setContents(byte[] contents) { this.contents = contents; } }
其次在創建一個類記錄上傳者的信息,信息有用戶名、主題、文件描述、已經上傳的文件
public class Ticket { private String customerName; private String subject; private String body;private Map<String, Attachment> attachments = new LinkedHashMap(); public Ticket() { } public String getCustomerName() { return this.customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public String getSubject() {return this.subject; } public void setSubject(String subject) { this.subject = subject; } public String getBody() { return this.body; } public void setBody(String body) { this.body = body; } public Attachment getAttachment(String name) { return (Attachment)this.attachments.get(name); } public Collection<Attachment> getAttachments() { return this.attachments.values(); } public void addAttachment(Attachment attachment) { this.attachments.put(attachment.getName(), attachment); } public int getNumberOfAttachments() { return this.attachments.size(); } }
2、頁面邏輯
這個demo將會實現三個頁面
- 默認首頁,提供跳轉去上傳頁面的鏈接,以及已經上傳的文件列表,文件列表中文件的主題名將鏈接到文件的詳細信息頁面
- 上傳文件頁面,這裏客戶可以填寫文件的詳細信息,並選擇文件上傳,點擊Submit提交之後將會進入文件的詳細信息頁面,表示文件成功上傳
- 文件詳細信息頁面會展示文件的詳細信息,並提供文件下載鏈接,和返回第一個頁面的鏈接
3、代碼邏輯
- 使用一個LinkedHashMap保存已經上傳的文件信息,使用一個volatile類型的變量記錄文件的id。
- 使用action進行頁面的重定向,以及上傳、下載功能。
public class TicketServlet extends HttpServlet { private volatile int TICKET_ID_SEQUENCE = 1; private Map<Integer, Ticket> ticketDatabase = new LinkedHashMap<>(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if(action == null) { action = "list"; } System.out.println(action); switch(action) { case "create": //進入上傳文件頁面 this.showTicketForm(request, response); break; case "view": //進入文件詳細信息頁面 this.viewTicket(request, response); break; case "download": //實現下載功能 this.downloadAttachment(request, response); break; case "list": default: //進入默認頁面 this.listTickets(request, response); break; } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if(action == null) { action = "list"; } switch(action) { case "create": //實現上傳功能 this.createTicket(request, response); break; case "list": default: //進入默認頁面 response.sendRedirect("tickets"); break; } }
4、三個頁面的JSP
- 默認界面,將接收來自請求的ticketDatabase這個map,然後通過判斷這個map的大小展示已上傳文件列表,還有就是還提供了兩個鏈接修改action的值實現重定向
<%@ page session="false" import="java.util.Map" %> <% @SuppressWarnings("unchecked") Map<Integer, Ticket> ticketDatabase = (Map<Integer, Ticket>)request.getAttribute("ticketDatabase"); %> <!DOCTYPE html> <html> <head> <title>Customer Support</title> </head> <body> <h2>Tickets</h2> <a href="<c:url value="/tickets"> <c:param name="action" value="create" /> </c:url>">Create Ticket</a><br /><br /> <% if(ticketDatabase.size() == 0) { %><i>There are no tickets in the system.</i><% } else { for(int id : ticketDatabase.keySet()) { String idString = Integer.toString(id); Ticket ticket = ticketDatabase.get(id); %>Ticket #<%= idString %>: <a href="<c:url value="/tickets"> <c:param name="action" value="view" /> <c:param name="ticketId" value="<%= idString %>" /> </c:url>"><%= ticket.getSubject() %></a> (customer: <%= ticket.getCustomerName() %>)<br /><% } } %> </body> </html>
- 文件上傳頁面,提供用戶填寫信息的輸入框,提供點擊Submit按鈕進行跳轉和上傳文件,將action的值賦值為create
<%@ page session="false" %> <!DOCTYPE html> <html> <head> <title>Customer Support</title> </head> <body> <h2>Create a Ticket</h2> <form method="POST" action="tickets" enctype="multipart/form-data"> <input type="hidden" name="action" value="create"/> Your Name<br/> <input type="text" name="customerName"><br/><br/> Subject<br/> <input type="text" name="subject"><br/><br/> Body<br/> <textarea name="body" rows="5" cols="30"></textarea><br/><br/> <b>Attachments</b><br/> <input type="file" name="file1"/><br/><br/> <input type="submit" value="Submit"/> </form> </body> </html>
- 文件詳細信息頁面,打印文件的詳細信息,提供下載鏈接(這個鏈接將action值修改為download),返回默認頁面的鏈接
<%@ page session="false" %> <% String ticketId = (String)request.getAttribute("ticketId"); Ticket ticket = (Ticket)request.getAttribute("ticket"); %> <!DOCTYPE html> <html> <head> <title>Customer Support</title> </head> <body> <h2>Ticket #<%= ticketId %>: <%= ticket.getSubject() %></h2> <i>Customer Name - <%= ticket.getCustomerName() %></i><br /><br /> <%= ticket.getBody() %><br /><br /> <% if(ticket.getNumberOfAttachments() > 0) { %>Attachments: <% int i = 0; for(Attachment a : ticket.getAttachments()) { if(i++ > 0) out.print(", "); %><a href="<c:url value="/tickets"> <c:param name="action" value="download" /> <c:param name="ticketId" value="<%= ticketId %>" /> <c:param name="attachment" value="<%= a.getName() %>" /> </c:url>"><%= a.getName() %></a><% } %><br /><br /><% } %> <a href="<c:url value="/tickets" />">Return to list tickets</a> </body> </html>
5、具體方法的實現
- listTickets方法將會把這個map傳遞給一會將要運行的JSP,然後通過方法getRequestDispatcher可以獲得一個javax.servlet.RequestDispatcher,這個對象將用於處理針對指定路徑的內部轉發和包含,通過該對象,將請求轉發給調用forward方法的listTickets.jsp
private void listTickets(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("ticketDatabase", this.ticketDatabase); request.getRequestDispatcher("/WEB-INF/jsp/view/listTickets.jsp") .forward(request, response); }
- showTicketForm方法將同樣調用getRequestDispatcher,將請求轉發給ticketForm.jsp
private void showTicketForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp") .forward(request, response); }
- createTicket方法將在上傳頁面點擊Submit之後調用,通過request.getParameter獲取請求的參數,創建一個Ticket對象並設置其CustomerName、Subject、Body,之後使用request.getParts()獲取上傳的文件,獲取一個Part對象filePart,調用processAttachment()方法將這個Part對象轉化為Attachment對象,然後添加到Ticket對象中,再然後將TICKET_ID_SEQUENCE加一作為id放入Map中,然後重定向到文件的詳細信息頁面
private void createTicket(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Ticket ticket = new Ticket(); ticket.setCustomerName(request.getParameter("customerName")); ticket.setSubject(request.getParameter("subject")); ticket.setBody(request.getParameter("body")); Part filePart = request.getPart("file1"); if(filePart != null && filePart.getSize() > 0) { Attachment attachment = this.processAttachment(filePart); if(attachment != null) { ticket.addAttachment(attachment); } } int id; synchronized(this) { id = this.TICKET_ID_SEQUENCE++; this.ticketDatabase.put(id, ticket); } response.sendRedirect("tickets?action=view&ticketId=" + id); }
這裏的processAttachment方法實現了將這個Part對象轉化為Attachment對象,具體是先從Part獲得InputStream,並將其復制到Attachment對象中。然後還使用了getSubmittedFileName()獲取文件名。
private Attachment processAttachment(Part filePart) throws IOException { InputStream inputStream = filePart.getInputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); int read; final byte[] bytes = new byte[1024]; while((read = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, read); } Attachment attachment = new Attachment(); attachment.setName(filePart.getSubmittedFileName()); attachment.setContents(outputStream.toByteArray()); return attachment; }
- viewTicket方法首先是獲取id,然後根據這個id調用getTicket方法獲取到Ticket對象。獲取到Ticket對象之後把這個對象和id一起以及請求轉發給viewTicket.jsp
private void viewTicket(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String idString = request.getParameter("ticketId"); Ticket ticket = this.getTicket(idString, response); if(ticket == null) { return; } request.setAttribute("ticketId", idString); request.setAttribute("ticket", ticket); request.getRequestDispatcher("/WEB-INF/jsp/view/viewTicket.jsp") .forward(request, response); }
其次這個根據id獲取Ticket的getTicket方法如果有什麽錯誤將會被重定向到/tickets頁面
private Ticket getTicket(String idString, HttpServletResponse response) throws ServletException, IOException { if(idString == null || idString.length() == 0) { response.sendRedirect("tickets"); return null; } try { Ticket ticket = this.ticketDatabase.get(Integer.parseInt(idString)); if(ticket == null) { response.sendRedirect("tickets"); return null; } return ticket; } catch(Exception e) { response.sendRedirect("tickets"); return null; } }
- downloadAttachment方法這個方法首先是根據id獲取Ticket對象,然後從請求中獲取文件的名字,根據這個name從Ticket中獲取文件的Attachment對象。
private void downloadAttachment(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String idString = request.getParameter("ticketId"); Ticket ticket = this.getTicket(idString, response); if(ticket == null) { return; } String name = request.getParameter("attachment"); if(name == null) { response.sendRedirect("tickets?action=view&ticketId=" + idString); return; } Attachment attachment = ticket.getAttachment(name); if(attachment == null) { response.sendRedirect("tickets?action=view&ticketId=" + idString); return; } response.setHeader("Content-Disposition", "attachment; filename=" + attachment.getName()); response.setContentType("application/octet-stream"); ServletOutputStream stream = response.getOutputStream(); stream.write(attachment.getContents()); }
最後的這幾行代碼,用於處理瀏覽器的下載請求,響應中設置頭Content-Disposition,將強制瀏覽器詢問客戶是保存還是下載文件,而不是在瀏覽器從查看文件,設置的文件類型是通用的/二進制內容類型的,這樣容器就不會使用字符編碼對該數據進行處理。最後使用ServletOutputStream將文件內容輸出到響應中。如果希望實現大文件下載,應該將數據從文件的InputStream中復制到ResponseOutputStream ,並且經常刷新ResponseOutputStream,這樣數據才能不斷被發送到用戶瀏覽器中。
JavaWeb——JSP開發2