话说:
昨天把分页作为礼物送给了自己,今天文件上传和昨天的一气呵成。
昨天的新闻没有上传功能,今天总结下;整体修改还是建立在上一篇分页基础之上。
目标:文件上传
围绕4个问题:
Q1:如何自定义文件名?
Q2: 如何获取文件后缀名?
Q3:如何获取文件存储路径(Web服务器和工程目录下各一份)?
Q4:如何将文件路径要存储到数据库表中的pic字段中?
前期准备:
1)在数据库里面新建一列pic,因为反射机制不能默认为null,所以通通把这一列设置为”images/default.jpg”;
存图片的列需要设置成BLOB、MEDIUMBLOB或LONGBLOB等数据类型
但是一般我们肯定不直接在数据库存图片楼,节省速度。
所以,我们存放图片路径。
update t_news set pic= “images/default.jpg”;
2)为了保证发布新闻界面newsAdd.jsp页面在提交表单的时候,可以提交文件,所以要新增表单属性:enctype=”multipart/form-data”;同时在newsServlet中新增注解:@MultipartConfig。这样就保证了页面添加文件时候,Servlet可以顺利接收到。
newsAdd.jsp
<%--
Created by IntelliJ IDEA.
User: Meice
Date: 2017/10/1
Time: 15:02
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>发布新闻</title>
</head>
<body>
<form action="news.do" method="post" enctype="multipart/form-data">
<%--传递参数的时候,可以直接传参,也可以用隐藏域--%>
<input type="hidden" name="op" value="add"/>
<table>
<tr>
<td>标题</td>
<td><input type="text" name="title"></td>
</tr>
<tr>
<td>作者</td>
<td><input type="text" name="author"></td>
</tr>
<tr>
<td>封面</td>
<td><input type="file" name="pic" alt="尚未提交文件"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="发布"></td>
</tr>
</table>
</form>
</body>
</html>
以上新增了封面这一行,结果是这样的:
好!准备工作到此结束,正式开始搞定4个问题。
Q1:如何自定义文件名?
为避免用户上传文件名相同,所以我们要按照自己规则来重命名文件,类似QQ截图那样。
同样,我们也作为一个工具类,封装成方法getFileName()
public static String getFileName(String suffix) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String now = sdf.format(new Date());
return "news"+now+suffix;
}
效果是这样的:news20171015171838.png;这样也就搞定了文件前缀名。
//获取文件File;但是用户上传的文件如何获取呢?
Part part = req.getPart("pic");
part.write("F:\\enheng.exe");
String header = part.getHeader("content-disposition");
part是个接口,通过part的getHeader()方法可以获取的一个key-value的一串名字:类似下面这样:form-data; name=”pic”; filename=”02_BaseDao再设计.exe”
我们需要的是filename,所以通过截取获得后缀名。(前办部分我们按照时间戳自定义,避免用户传入文件名称相同)
Q2:如何获取文件后缀名?
前面header就是一个包含文件完整名称的key-value键值队,截取出后缀名即可。为便于后面运用,我们封装为一个方法getSuffix()
//定义获取文件后缀的方法getSuffix()
private String getSuffix(String filename){
//详细过程看main()方法测试
String subStr = filename.substring(filename.indexOf("filename"),filename.lastIndexOf("\""));
subStr = subStr.substring(subStr.indexOf("\"")+1);
subStr = subStr.substring(subStr.indexOf("."));
return subStr;
}
这样,不论你传输什么文件,后缀名都可以搞定了。jpg,jpeg,gif都可以,视频也可以的,只不过,页面不好显示。
Q3:如何获取文件存储路径(Web服务器和工程目录下各一份)?
为什么要获取绝对路径呢?因为不同浏览器有的支持绝对路径,有的不支持。
要是再Linux中就好了。
项目路径,直接可以查看,我们定义为proPath
项目在Tomcat服务器部署路径,通过getRealPath()方法可以获取,定义为realPath
String proPath ="D:\\Users\\Administrator\\IdeaProjects\\Meice_idea_utimate\\JavaWeb_Servlet_Paging\\web\\images";
//获取项目在服务器部署后的真实路径;服务器也会迁移啊。Linux就不用了在部署Web服务器对应Images目录下再次写入一次
String realPath = req.getSession().getServletContext().getRealPath("images");
Q4:如何将文件路径要存储到数据库表中的pic字段中?
//相对路径用于存储表中pic字段
String filename = FileUploadUtil.getFileName(getSuffix(header));
String relPath = "images/"+ filename;
//写入到项目文件中
part.write(proPath+"/"+filename);
.....
news.setPic(relPath);
同时,我们只需要稍作修改这几个地方,整个图片上传就搞定啦!
1、news.setPic(relPath); 为pic字段设置属性,路径就是images/我们自定义的文件名。一旦用户点击上传,项目路径下会按照我们的命名规则产生新的文件:类似这样:news20171015205739.jpg
项目一旦部署,会在工程对应的Web服务器端再次生成这个文件。
2、修改newsUpdate2()方法,创建对象的时候,新增pic属性即可
//法2:封装发布新闻的方法(前后对比明显吧!)
public int newsAdd2(News news){
String sql = "insert into t_news (title,author,pic) values (?,?,?)";
Object[] params = {news.getTitle(),news.getAuthor(),news.getPic()};
return CUD(sql,params);
}
3、最后,在显示页面newsShow.jsp,显示图片。
<td align="center"><img src="${news.pic}" width="60px" height="60px"></td>
4、调试过程,可能报一下错误:
这个时候,我在部署后的路径下找到了上传的图片;但是在工程images下面没有找到。
奇怪的是,这里报错是从C:根目录下开始找的……;
这里是正常的,在IDEA里面,就是会自己在生成一个路径。经过排查,发现不能同时给两个位置写入文件。
//写入到项目文件中
part.write(proPath+"/"+filename);
//写入到Tomcat服务器中
// part.write(realPath+"/"+filename);
只有写入项目中,然后重新部署,刷新就可以显示啦!
以下是完整代码:
主要修改的就是newsServlet、我们自己定义的工具类FileUploadUitl(重命名文件)和getSuffix()方法(获取文件后缀名)
实体类News
package com.hmc.jdbc.news.model;
public class News {
private int id;
private String title;
private String author;
private String pic;
public News() {
}
public News(int id, String title, String author) {
this.id = id;
this.title = title;
this.author = author;
}
public News(int id, String title, String author, String pic) {
this.id = id;
this.title = title;
this.author = author;
this.pic = pic;
}
//没有2个参数的构造方法,我自己就造一个出来;因为增加新闻不需要考虑ID,对于客户来说。
public News(String title, String author) {
this.title = title;
this.author = author;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
@Override
public String toString() {
return "News{" +
"id=" + id +
", title='" + title + '\'' +
", author='" + author + '\'' +
", pic='" + pic + '\'' +
'}';
}
}
以上仅仅增加了pic这个属性,生成了set(),get()方法。
NewsServlet
package com.hmc.jdbc.news.servlet;
import com.hmc.jdbc.news.dao.BaseDao;
import com.hmc.jdbc.news.dao.NewsDao;
import com.hmc.jdbc.news.model.News;
import com.hmc.jdbc.news.model.Pager;
import com.hmc.jdbc.news.util.FileUploadUtil;
import com.hmc.jdbc.news.util.StringConvertUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.sql.Connection;
import java.util.List;
/**
* User:Meice
* 2017/10/5
*/
@WebServlet(urlPatterns = "/news.do")
@MultipartConfig
public class NewsServlet extends BaseServlet {
NewsDao nd = new NewsDao();
BaseDao bd = new BaseDao();
//查(R)
// 未避免代码混乱,把显示新闻列表封装成方法;这里采用最高级的显示新闻方法listShow3()
private void list (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String sql = "select * from t_news";
List<News> list =(List<News>) bd.listShow3(sql,News.class,null);
//3 存储到req中
req.setAttribute("list", list);
req.getRequestDispatcher("newsShow.jsp").forward(req, resp);
}
//定义查询分页显示页面的方法pageList()
private void pageList(HttpServletRequest req,HttpServletResponse resp ) {
//接收参数pageIndex
String strPageIndex = req.getParameter("pageIndex");
String strPageSize = req.getParameter("pageSize");
int pageIndex = StringConvertUtil.getVal(strPageIndex);
int pageSize = StringConvertUtil.getVal(strPageSize);
//当时转换方法里面如果为null返回的是0
pageIndex = pageIndex ==0?1:pageIndex;
pageSize = pageSize == 0?5:pageSize;
Pager<News> pager = new Pager<>();
//避免最后页会多出来
pager.setCount(nd.count());
pager.setPageIndex(pageIndex);
pager.setPageSize(pageSize);
//调用pageList()方法
pager = nd.pageList(pager);
//存储到req中
req.setAttribute("pager",pager);
//页面跳转
try {
req.getRequestDispatcher("newsShow.jsp").forward(req,resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//增(C)
private void add(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {
//接受参数
String title = req.getParameter("title");
String author = req.getParameter("author");
//获取文件File
Part part = req.getPart("pic");
//获取part头信息
String header = part.getHeader("content-disposition");
//这个路径是我们项目的images所在路径;在本项目路径下写入一次
String proPath ="D:\\Users\\Administrator\\IdeaProjects\\Meice_idea_utimate\\JavaWeb_Servlet_Paging\\web\\images";
//获取项目在服务器部署后的真实路径;服务器也会迁移啊。Linux就不用了在部署Web服务器对应Images目录下再次写入一次
String realPath = req.getSession().getServletContext().getRealPath("images");
//D:\Users\Administrator\IdeaProjects\Meice_idea_utimate\out\artifacts\JavaWeb_Servlet_Paging_war_exploded\images
//相对路径用于存储表中pic字段
String filename = FileUploadUtil.getFileName(getSuffix(header));
String relPath = "images/"+ filename;
//写入到项目文件中
part.write(proPath+"/"+filename);
//写入到Tomcat服务器中
// part.write(realPath+"/"+filename);
News news = new News(title,author);
news.setPic(relPath);
int result = nd.newsAdd2(news);//(写这些方法从后往前写)
if(result > 0) {
req.getRequestDispatcher("news.do?op=pageList").forward(req,resp);
}else {
req.getRequestDispatcher("newsAdd.jsp").forward(req,resp);
}
}
//定义获取文件后缀的方法getSuffix()
private String getSuffix(String filename){
//详细过程看main()方法测试
String subStr = filename.substring(filename.indexOf("filename"),filename.lastIndexOf("\""));
subStr = subStr.substring(subStr.indexOf("\"")+1);
subStr = subStr.substring(subStr.indexOf("."));
return subStr;
}
//改(U)
//显示要修改的新闻
public void changeShow(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {
//接受参数
String strId = req.getParameter("id");
int id = StringConvertUtil.getVal(strId);
News news = nd.newsPut3(id);
req.setAttribute("news",news);
req.getRequestDispatcher("newsUpdate.jsp").forward(req,resp);
}
//执行修改操作
public void change(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {
//接受参数
String strId = req.getParameter("id");
int id = StringConvertUtil.getVal(strId);
String title = req.getParameter("title");
String author = req.getParameter("author");
News news = new News(id,title,author);
//调用方法
int result = nd.newsUpdate2(news);
if(result >0) {
req.getRequestDispatcher("news.do?op=pageList").forward(req,resp);
}else {
req.getRequestDispatcher("newsUpdate.jsp").forward(req,resp);
}
}
//删(D)
private void del(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {
//接受参数
String strId = req.getParameter("id");
int id = StringConvertUtil.getVal(strId);
int result = nd.newsDel2(id);
req.getRequestDispatcher("news.do?op=pageList").forward(req,resp);
}
public static void main(String[] args) {
//form-data; name="pic"; filename="02_BaseDao再设计.exe"
String str = "form-data; name=\"pic\"; filename=\"02_BaseDao再设计.exe\"";
System.out.println(str.substring(str.indexOf("filename"),str.lastIndexOf("\"")));
String subStr = str.substring(str.indexOf("filename"),str.lastIndexOf("\""));
//filename="02_BaseDao再设计.exe
subStr = subStr.substring(subStr.indexOf("\"")+1);
System.out.println(subStr);
//"02_BaseDao再设计.exe 02_BaseDao再设计.exe
subStr = subStr.substring(subStr.indexOf("."));
System.out.println(subStr);
//截取过程如下
/*
filename="02_BaseDao再设计.exe
02_BaseDao再设计.exe
.exe
*/
}
}
FileUpLoad()工具类
package com.hmc.jdbc.news.util;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* User:Meice
* 2017/10/15
*/
public class FileUploadUtil {
public static String getFileName(String suffix) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String now = sdf.format(new Date());
return "news"+now+suffix;
}
public static void main(String [] args) {
System.out.println(getFileName(".png"));
}
}
newsShow.jsp
<%@ page import="java.util.List" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="com.hmc.jdbc.news.model.News" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%--
Created by IntelliJ IDEA.
User: Meice
Date: 2017/10/1
Time: 13:30
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--如果不用jstl,还是采用JSP嵌套获取--%>
<%--
<%
//本来在页面,我们获取list集合非常简单,${}这样就获取了,但是实际过程中,我们在JSP代码中没发做到用这个来获取
List<News> list =(List<News>) request.getAttribute("list");
%>
--%>
<html>
<head>
<title>Meice的新闻列表</title>
</head>
<body>
<h2 align="center">Meice的新闻列表</h2><br/>
<a href="newsAdd.jsp">发布新闻</a>
<table border="1" align="center" width="80%">
<thead>
<tr>
<th>编号</th>
<th>标题</th>
<th>作者</th>
<th>图片</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%--${news}--%>
<%--这里传过来就是那幅图的场景;现在的问题是我不知道怎么接收这个传递过来的值,直接用EL取出来的是
个List<News>的集合。这边又没法遍历。??
如果不用JSTL,我们用普通的for each,问题是怎么取出这个值。
--%>
<c:forEach var="news" items="${pager.datas}">
<tr>
<td align="center">${news.id}</td>
<td align="center">${news.title}</td>
<td align="center">${news.author}</td>
<td align="center"><img src="${news.pic}" width="60px" height="60px"></td>
<td align="center">
<a href="news.do?op=changeShow&id=${news.id}">修改</a>
<a href="news.do?op=del&id=${news.id}" onclick="return confirm('真的忍心删我?')">删除</a>
</td>
</tr>
</c:forEach>
<tr>
<td colspan="5" align="center">
<div style="float: left;margin-left: 150px">
<c:if test="${pager.pageIndex>1}">
<a href="news.do?op=pageList&pageIndex=1">首页</a>
<a href="news.do?op=pageList&pageIndex=${pager.pageIndex-1}">上一页</a>
</c:if>
<c:forEach var="pn" begin="${pager.start}" end="${pager.end}">
<c:if test="${pn==pager.pageIndex}">
${pn}
</c:if>
<c:if test="${pn != pager.pageIndex}">
<a href="news.do?op=pageList&pageIndex=${pn}">${pn}</a>
</c:if>
</c:forEach>
<%--EL表达式支持运算--%>
<c:if test="${pager.pageIndex<pager.totalPage}">
<a href="news.do?op=pageList&pageIndex=${pager.pageIndex+1}">下一页</a>
<a href="news.do?op=pageList&pageIndex=${pager.totalPage}">末页</a>
${pager.pageIndex}/${pager.totalPage}
</c:if>
</div>
<%--以下实现页面跳转--%>
<form action="news.do?op=pageList" method="post" style="float:right;margin-right: 200px" >
<input type="text" name="pageIndex" style="width: 30px;height: 30px" value="${param.pageIndex}" />
<select name="pageSize" style="height: 30px">
<option value="5" ${param.pageSize==5?'selected':''}>5</option>
<option value="10" ${param.pageSize==10?'selected':''}>10</option>
<option value="20" ${param.pageSize==20?'selected':''}>20</option>
</select>
<input type="submit" value="Go"style="float: right;height: 30px">
</form>
</td>
</tr>
</tbody>
<tfoot></tfoot>
</table>
</body>
</html>
其他代码都和上一篇博客一样。
最终效果图:
总结:
主要就是解决文章开头的3个问题。顺着问题找解决方案,目的就很明确。
1、实体类News中封装一个存放图片的属性pic即可,调用方式和其他属性类似;
2、通过request的getPart()方法,返回一个part对象,调用getHeader()方法,根据需要截取文件后缀和重命名文件;
3、part.write()方法可以把我们命名好的文件,存入到工程目录和服务器目录下;
4、把命名好的文件名赋值给变量relPath,传递到页面;
5、修改对应的newsUpdate2()方法,页面newsShow.jsp做对应修改即可。
好了,各位读者,下期见!晚安!

1342

被折叠的 条评论
为什么被折叠?



