使用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的更多相关文章

  1. JavaWeb——JSP开发1

    1.什么是jsp,为什么要使用jsp. 再使用idea创建完一个web工程后,在webapp目录下会生成一个index.jsp 直接编译运行,网站将自动打开这样一个网页: 所以我们可以推测这个inde ...

  2. javaweb的开发模式

    SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式. 一.JSP+JavaBean开发模式 1 ...

  3. Java之JavaWeb项目开发开始准备

    操作系统:Mac OS 10.11.6 Tomcat版本:9.0.0.M17 前言:部署Tomcat可以参考我一年前做的笔记:<在MAC下搭建JSP开发环境>,也可以参考大神写的挺好的&l ...

  4. 2017.11.6 JavaWeb-----第七章 JavaWeb常用开发模式与案例

    JavaWeb-----第七章 JavaWeb常用开发模式与案例 (1)单纯的JSP页面开发模式 通过在JSP中的脚本标记,直接在JSP页面中实现各种功能.称为"单纯的JSP页面编程模式&q ...

  5. JavaWeb:JSP技术基础

    JavaWeb:JSP技术 快速开始 介绍 JSP全称Java Server Pages,是一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%> ...

  6. IntelliJ IDEA安装及jsp开发环境搭建

    一.前言 现在.net国内市场不怎么好,公司整个.net组技术转型,就个人来说还是更喜欢.net,毕竟不是什么公司都像微软一样财大气粗开发出VS这样的宇宙级IDE供开发者使用,双击sln即可打开项目, ...

  7. JSP开发环境配置问题解答

    有过JSP开发经验的同学对于JSP开发环境的配置一定非常的很有感触,十分的繁琐,有时因为一个小的问题导致我们配置的配置前功尽弃,本篇我将重点带领大家一起探讨一下关于JSP环境配置的一些常见问题,及解决 ...

  8. windows上JSP开发环境全搭建

    JSP开发环境全搭建 最近需要用到JSP做项目,所以要配置JSP的开发环境,总结一下配置步骤以备以后再配置需要. 配置JAVA开发环境,配置JDK 下载JDK,在这里下载开发所需的JDK,可以根据自己 ...

  9. 一.JSP开发的工具下载与环境搭建

    JSP技术的强势: (1)一次编写,到处运行.在这一点上Java比PHP更出色,除了系统之外,代码不用做任何更改. (2)系统的多平台支持.基本上可以在所有平台上的任意环境中开发,在任意环境中进行系统 ...

随机推荐

  1. django基础知识之自连接:

    自连接 对于地区信息,属于一对多关系,使用一张表,存储所有的信息 类似的表结构还应用于分类信息,可以实现无限级分类 新建模型AreaInfo,生成迁移 class AreaInfo(models.Mo ...

  2. Linux 文件系统的基本结构

    Linux文件系统为一个倒置的树状结构,所有文件或文件夹均包含在一个根目录/中. Linux系统严格区分大小写所以在Linux中:一个名为“A”的文件夹和一个名为“a”的文件夹是不同的两个文件夹,这点 ...

  3. 宏旺半导体深度剖析嵌入式存储芯片eMMC原理 一篇概括大全

    eMMC 一直是嵌入式存储市场最主流的选择,除了读写速度快.性价比高外,在节省空间方面也是相当优秀,今天宏旺半导体就和大家详细聊聊eMMC. eMMC 是 embedded MultiMediaCar ...

  4. 【题解】长度为素数的路径个数-C++

    Description 对于正整数n (3≤n<20),可以画出n阶的回形矩阵.下面画出的分别是3阶的,4阶的和7阶的回形矩阵: 对于n阶回形矩阵,从左上角出发,每步可以向右或向下走一格,走2* ...

  5. Guid几种格式及之间的互换,以及利用Base64缩短guid的长度到22个字符和还原

    1.Guid.NewGuid().ToString("N") 结果为: 38bddf48f43c48588e0d78761eaa1ce6 2.Guid.NewGuid().ToSt ...

  6. Git对象

    上一节了解了 Git 的一个重要的概念:暂存区. 暂存区是一个介于工作区和版本库的中间状态,当执行commit时,实际上是将暂存区的内容提交大版本库中,而执行add则是将本次变更添加到暂存区. 上一节 ...

  7. VUE过滤器的使用 vue 时间格式化

    过滤器介绍 官方教程地址:https://cn.vuejs.org/v2/guide/filters.html 过滤器常被用于一些文本格式化 我们可以自定义过滤器,可以实现各种各样的功能. vue时间 ...

  8. 十三、asp.net中Repeater控件用法笔记

    大家可能都对datagrid比较熟悉,但是如果在数据量大的时候,我们就得考虑使用 repeater作为我们的数据绑定控件了.Repeater控件与DataGrid (以及DataList)控件的主要区 ...

  9. 转 java - java反射机制创建对象

    转 https://blog.csdn.net/nch_ren/article/details/78814080 1.创建service实现类 package com.springstudy.refl ...

  10. 基于SpringBoot从零构建博客网站 - 新增创建、修改、删除专栏功能

    守望博客是支持创建专栏的功能,即可以将一系列相关的文章归档到专栏中,方便用户管理和查阅文章.这里主要讲解专栏的创建.修改和删除功能,至于专栏还涉及其它的功能,例如关注专栏等后续会穿插着介绍. 1.创建 ...