1. 程式人生 > >JavaWeb——JSP開發2

JavaWeb——JSP開發2

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