使用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. java判断年份是否为闰年

    在t1.jsp 中,设置一个表单,可以输入年份,提交到另外一个action进行计算,如果算出来是闰年,那么就跳转到a1.jsp(显示闰年),如果是平年就跳转到a2.jsp(显示平年). 要求:需要把计 ...

  2. [AI开发]目标跟踪之行为分析

    基于视频结构化的应用中,目标在经过跟踪算法后,会得到一个唯一标识和它对应的运动轨迹,利用这两个数据我们可以做一些后续工作:测速(交通类应用场景).计数(交通类应用场景.安防类应用场景)以及行为检测(交 ...

  3. 嵊州D1T3 睡美人航班

    嵊州D1T3 睡美人航班 不知不觉中,我对她的爱意已经达到了 n. 是这样子的,第 1 分钟,我对她的爱意值是 (1, 1). 假如当第 x 分钟时我对她的爱意值是 (a, b),那么第 x + 1 ...

  4. (ps2019)Photoshop 2019 最新破解版下载

    Photoshop CC 2019新增功能: 下载地址 点我 新功能介绍:https://helpx.adobe.com/cn/photoshop/using/whats-new.html 经过改良设 ...

  5. CDQZ集训DAY4 日记

    早上起来之后发现座位被zzh占了,得知座位改为先来后到,什么鬼…… 于是去了另一个有耳机的机房,然而并没有什么卵用. T1上来感觉很有意思,先切50分再说.T2好像是原题的说,切了原题30分后大胆猜测 ...

  6. nu.xom:Element

    Element: 机翻 Element(Element element) :通过深复制,创建一个element Element(String name) :创建一个没有命名空间的element Ele ...

  7. Scrum是脆弱的,不敏捷的

    正如标题所示,这篇文章是关于 Scrum 的两个不同方面.第一部分涉及 Scrum 不敏捷,第二部分涉及 Scrum 脆弱. 在详细介绍之前,简短的免责声明:我在这篇文章(以及一般博客中)中提出的所有 ...

  8. 简单函数编写_strcpy、_stroverchg、_strcmp

    字符串复制函数 void _strcpy(char *tar, const char * res) { char *p = tar; while(assert(tar && res), ...

  9. 跟着大彬读源码 - Redis 5 - 对象和数据类型(上)

    相信很多人应该都知道 Redis 有五种数据类型:字符串.列表.哈希.集合和有序集合.但这五种数据类型是什么含义?Redis 的数据又是怎样存储的?今天我们一起来认识下 Redis 这五种数据结构的含 ...

  10. Linux学习之安装jdk

    下载jdk for linux jdk for linux oracle download 卸载已有的jdk (1)查询是否安装java软件: rpm -qa|grep java (2)卸载jdk: ...