Java实现下载BLOB字段中的文件
概述
web项目的文件下载实现;servlet接收请求,spring工具类访问数据库及简化大字段内容获取。
虽然文章的demo中是以sevlet为平台,想必在spring mvc中也有参考意义。
核心代码
响应设置和输出
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
/* 1. 设置响应内容类型 */
response.setContentType("Application/Octet-stream;charset=utf-8"); /* 2. 读取文件 */
String fileName = ... // 获取文件名
fileName = new String(file.getName().getBytes(), "ISO-8859-1");
InputStream is = ... // 获取文件流 /* 3. 将文件名加入响应头 */
String cd = "attachment; filename=${fileName}"
.replaceFirst("\\$\\{fileName\\}", fileName);
((HttpServletResponse) response).addHeader("Content-Disposition", cd); /* 4. 将内容写到指定输出流,设置响应内容长度 */
OutputStream os = response.getOutputStream();
int length = org.springframework.util.FileCopyUtils.copy(is, os);
response.setContentLength(length); // 不设置长度也可 /* 5. 关闭输出流 */
os.close();
}
Servlet
我们定义一个servlet用于接收文件下载的请求,按照上述代码实现文件下载服务。但是我们遗留了两个问题:
- 如何获取文件名
- 如何获取文件的输入流
虽然这两个问题并非难解,我们依然提供一个参考;考虑到文件可能存放在文件服务器或者数据库等多种形式,这里仅提供基于数据库的获取方案。
获取文件名和文件内容
/* 创建JdbcTemplate用以查询数据 */
org.springframework.jdbc.core.JdbcTemplate jt = new org.springframework.jdbc.core.JdbcTemplate(ds);
// 以java.sql.DataSource实例作为参数 /* 创建LobHandler用以简化Lob字段读取 */
// 在内部对象的方法中使用,需声明为final
final org.springframework.jdbc.support.lob.LobHandler lobHandler = new org.springframework.jdbc.support.lob.DefaultLobHandler(); /* 创建Map用来存放文件名和文件内容 */
final Map file = new HashMap(); /* 确保可以查询到数据记录 - 取第一条 */
jt.query(
sql,
args,
new org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor() { protected void streamData(ResultSet rs)
throws SQLException, IOException,
DataAccessException {
// 注意,没有调用过rs.next();rs初始化已指向第一条记录
String fileName = rs.getString("filename");
InputStream is = lobHandler.getBlobAsBinaryStream(rs, "filecontent");
file.put("filename", fileName);
file.put("filecontent", is);
}
});
基于spring JdbcTemplate获取文件名和文件流
上面一段代码可以用来获取存在数据库的文件名和文件内容(BLOB字段):
- JdbcTemplate用来访问数据库
依赖于数据源实例。 - LobHandler用于简化Lob字段的读取
代码只是展示了针对BLOB字段的一种用法,关于CLOB的或其他方法,请查阅API。 - 匿名内部类
我们定义了基于AbstractLobStreamingResultSetExtractor的匿名内部类,并创建了对象实例。实例的方法中所访问的实例外的变量,要求必须是final类型的,所以我们把LobHandler定义成final的。鉴于同样的原因,我们无法直接把实例内部查询到的文件名和文件内容,直接赋值给外部的变量(因为外部的必须是final的),所以我们定义了一个final Map对象,用来存放结果。
代码优化
包图

包说明:
- cn.com.hnisi.fzyw.xzfy.gz.test.servlet
集中管理servlet - cn.com.hnisi.fzyw.xzfy.gz.download.service
集中管理处理请求的service,及创建service实例的工厂 - cn.com.hnisi.fzyw.xzfy.gz.download.domain
集中管理pojo - cn.com.hnisi.baseservices.db
提供数据库访问功能
类图

数据库访问支持组件
DataSourceFactory
DataSource工厂,负责向JdbcTemplateFactory提供可用的DataSource实例。
package cn.com.hnisi.baseservices.db; import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource; import cn.com.hnisi.baseservices.config.Config;
/**
* SINOBEST 数据源工厂,用于获取DataSource.<br>
* 使用JNDI查找上下文中的DataSource.<br>
* @author lijinlong
*
*/
public class DataSourceFactory {
private static DataSourceFactory instance;
private static InitialContext context;
/** 默认的数据源的jndi名称属性的key */
private static final String DEFAULT_DATASOURCE_PROP_KEY = "DB.DATASOURCE"; private DataSourceFactory() {
super();
} /**
* 单例模式获取工厂实例.<br>
* @return
*/
public static final synchronized DataSourceFactory newInstance() {
if (instance == null)
instance = new DataSourceFactory();
return instance;
} /**
* 获取默认的DataSource.<br>
* 根据config/config.properties中DB.DATASOURCE属性值查找.<br>
* @return
*/
public DataSource getDefaultDataSource() {
String jndiName = Config.getInstance().getValue(DEFAULT_DATASOURCE_PROP_KEY);
DataSource ds = getDataSource(jndiName);
return ds;
} /**
* 根据JNDI名称,获取上下文中的DataSource.<br>
* @param jndiName
* @return
*/
public synchronized DataSource getDataSource(String jndiName) {
DataSource ds = null;
try {
if (context == null)
context = new InitialContext();
ds = (DataSource)context.lookup(jndiName);
} catch (NamingException e) {
e.printStackTrace();
}
return ds;
}
}
DataSourceFactory
JdbcTemplateFactory
JdbcTemplate工厂,负责创建可用的JdbcTemplate实例。
package cn.com.hnisi.baseservices.db; import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate;
/**
* 用于获取{@link JdbcTemplate}实例的工厂.<br>
* <ol>
* <li>使用{@link #getDefaultJT()}可以获取基于默认数据源({@link DataSourceFactory#getDefaultDataSource()})的JT实例.
* </li>
* <li>使用{@link #getJT(DataSource)}可以获取基于指定数据源的JT实例.
* </li>
* <li>使用{@link #getJT(String)}可以获取基于指定JNDI name的数据源的JT实例.
* </li>
* <li>
* 其他的可能以后会补充.
* </li>
* </ol>
* @author lijinlong
*
*/
public class JdbcTemplateFactory {
private static JdbcTemplateFactory instance; private JdbcTemplateFactory() {
super();
} public static synchronized JdbcTemplateFactory newInstance() {
if (instance == null)
instance = new JdbcTemplateFactory(); return instance;
} /**
* 使用默认的数据源,获取{@link JdbcTemplate}对象实例.<br>
* @return
*/
public JdbcTemplate getDefaultJT() {
DataSource ds = DataSourceFactory.newInstance().getDefaultDataSource();
JdbcTemplate jt = getJT(ds);
return jt;
} /**
* 使用指定的数据源,获取{@link JdbcTemplate}对象实例.<br>
* @param ds
* @return
*/
public JdbcTemplate getJT(DataSource ds) {
if (ds == null)
return null;
JdbcTemplate jt = new JdbcTemplate(ds);
return jt;
} /**
* 根据数据源的jndiName,构造{@link JdbcTemplate}对象实例.
* @param dsJndiName
* @return
*/
public JdbcTemplate getJT(String dsJndiName) {
if (dsJndiName == null || dsJndiName.length() == 0)
return null; DataSource ds = DataSourceFactory.newInstance().getDataSource(dsJndiName);
JdbcTemplate jt = getJT(ds);
return jt;
}
}
JdbcTemplateFactory
File组件
自定义pojo,存放文件的name和content。
package cn.com.hnisi.fzyw.xzfy.gz.download.domain;
import java.io.InputStream;
public class File {
private String name;
private InputStream is;
public File() {
super();
}
public File(String name, InputStream is) {
super();
this.name = name;
this.is = is;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public InputStream getIs() {
return is;
}
public void setIs(InputStream is) {
this.is = is;
}
}
File
数据库访问组件
IDownloadService
文件下载服务接口。
package cn.com.hnisi.fzyw.xzfy.gz.download.service;
import java.util.List;
import cn.com.hnisi.fzyw.xzfy.gz.download.domain.File;
public interface IDownloadService {
/**
* 读取文件.<br>
*
* @param sql
* 查询sql语句,必须包含文件名字段和文件内容字段.
* @param args
* 参数 - 确保sql查询的结果只有一条.
* @param colNameFileName
* 存放文件名的字段名,大写.
* @param colNameFileContent
* 存放文件内容的字段名,大写.
* @return
*/
public File read(final String sql, final Object[] args,
final String colNameFileName, final String colNameFileContent);
}
IDownloadService
DownloadServiceImpl
文件下载服务实现类。
package cn.com.hnisi.fzyw.xzfy.gz.download.service; import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler; import cn.com.hnisi.baseservices.db.JdbcTemplateFactory;
import cn.com.hnisi.fzyw.xzfy.gz.download.domain.File; public class DownloadServiceImpl implements IDownloadService {
/**
* SINOBEST common 文件下载实现.
*/
public File read(final String sql, final Object[] args,
final String colNameFileName, final String colNameFileContent) {
JdbcTemplate jt = JdbcTemplateFactory.newInstance().getDefaultJT();
final LobHandler lobHandler = new DefaultLobHandler();
final File file = new File();
jt.query(sql, args, new AbstractLobStreamingResultSetExtractor() { protected void streamData(ResultSet rs) throws SQLException,
IOException, DataAccessException {
file.setName(rs.getString(colNameFileName));
file.setIs(lobHandler.getBlobAsBinaryStream(rs,
colNameFileContent));
}
});
return file;
} }
DownloadServiceImpl
DownloadServiceFactory
文件下载服务工厂,创建服务组件实例。
package cn.com.hnisi.fzyw.xzfy.gz.download.service;
public class DownloadServiceFactory {
private static DownloadServiceFactory ds;
private DownloadServiceFactory() {
super();
}
public static synchronized DownloadServiceFactory newInstance() {
if (ds == null)
ds = new DownloadServiceFactory();
return ds;
}
public IDownloadService createDownloadService() {
IDownloadService ds = new DownloadServiceImpl();
return ds;
}
}
DownloadServiceFactory
Servlet
FileDownloadTestServlet
文件下载请求的servlet
package cn.com.hnisi.fzyw.xzfy.gz.test.servlet; import java.io.IOException;
import java.io.OutputStream; import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse; import org.springframework.util.FileCopyUtils; import cn.com.hnisi.fzyw.xzfy.gz.download.domain.File;
import cn.com.hnisi.fzyw.xzfy.gz.download.service.DownloadServiceFactory; public class FileDownloadTestServlet extends HttpServlet { private static final long serialVersionUID = -2168892287436159079L;
/**
* SINOBEST common 文件下载测试.
*/
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
/* 1. 设置响应内容类型 */
response.setContentType("Application/Octet-stream;charset=utf-8"); /* 2. 读取文件 */
String sql = "select CLMC, SQCL from V_FZYWGZ_JK_XZFYSQXX_CL where SYSTEMID=?";
String systemid = request.getParameter("systemid");
Object[] args = { systemid };
String colNameFileName = "CLMC";
String colNameFileContent = "SQCL";
File file = DownloadServiceFactory.newInstance()
.createDownloadService()
.read(sql, args, colNameFileName, colNameFileContent); /* 3. 将文件名加入响应头 */
String fileName = new String(file.getName().getBytes(), "ISO-8859-1");
((HttpServletResponse) response).addHeader("Content-Disposition",
"attachment; filename=" + fileName); /* 4. 将内容写到指定输出流,设置响应内容长度 */
OutputStream os = response.getOutputStream();
int length = FileCopyUtils.copy(file.getIs(), os);
response.setContentLength(length); /* 5. 关闭输出流 */
os.close();
}
}
FileDownloadTestServlet
Java实现下载BLOB字段中的文件的更多相关文章
- Java实现打包下载BLOB字段中的文件
概述 web项目的文件打包下载实现:servlet接收请求,spring工具类访问数据库及简化大字段内容获取,org.apache.tools.zip打包. 必要提醒:当前总结是继Java实现下载BL ...
- Java学习-040-级联删除目录中的文件、目录
之前在写应用模块,进行单元测试编码的时候,居然脑洞大开居然创建了一个 N 层的目录,到后来删除测试结果目录的时候,才发现删除不了了,提示目录过长无法删除.网上找了一些方法,也找了一些粉碎机,都没能达到 ...
- java 读写Oracle Blob字段
许久没有分享代码了,把这段时间写的一个Java操作Blob字段,有日子没写Java了,就当作笔记记录一下.1. [代码][Java]代码 跳至 [1] [全屏预览]package com.wa ...
- hadoop学习笔记(十):hdfs在命令行的基本操作命令(包括文件的上传和下载和hdfs中的文件的查看等)
hdfs命令行 ()查看帮助 hdfs dfs -help ()查看当前目录信息 hdfs dfs -ls / ()上传文件 hdfs dfs -put /本地路径 /hdfs路径 ()剪切文件 hd ...
- HttpClient使用之下载远程服务器中的文件(注意目录遍历漏洞)
参考文献: http://bbs.csdn.net/topics/390952011 http://blog.csdn.net/ljj_9/article/details/53306468 1.下载地 ...
- 用java 代码下载Samba服务器上的文件到本地目录以及上传本地文件到Samba服务器
引入: 在我们昨天架设好了Samba服务器上并且创建了一个 Samba 账户后,我们就迫不及待的想用JAVA去操作Samba服务器了,我们找到了一个框架叫 jcifs,可以高效的完成我们工作. 实践: ...
- Java将对象保存到文件中/从文件中读取对象
1.保存对象到文件中 Java语言只能将实现了Serializable接口的类的对象保存到文件中,利用如下方法即可: public static void writeObjectToFile(Obje ...
- 利用backgroundwork----递归读取网页源代码,并下载href链接中的文件
今天闲着没事,研究了一下在线更新程序版本的问题.也是工作中的需要,开始不知道如何下手,各种百度也没有找到自己想要的,因为我的需求比较简单,所以就自己琢磨了一下.讲讲我的需求吧.自己在IIs上发布了一个 ...
- ORACLE 向BLOB字段中出入图片等二进制文件,使用Oracle SQl Developer工具
使用PL/SQL也可以 create directory "image" as 'e:\'; --"image" 要带双引号,网上很多不带的,我测试时出错,并且 ...
随机推荐
- LightOJ 1085 - All Possible Increasing Subsequences 树状数组+离散
http://www.lightoj.com/volume_showproblem.php?problem=1085 题意:求一个序列的递增子序列个数. 思路:找规律可以发现,某个数作为末尾数的种类数 ...
- Tomcat报错:Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/JFreeChartTest]]
最好把项目移除,然后在tomcat的webapps发布路径下也把项目文件删掉,重新部署就好了,原因是可能在tomcat的remove覆盖中以前的文件有所保留导致冲突,亲测有效
- [HDU5214]Movie解题报告|小水题大智慧
Movie Cloud and Miceren like watching movies. Today, they want to choose some wonderful scenes from ...
- 【BZOJ】1419 Red is good
[算法]期望DP [题解]其实把状态表示出来就是很简单的期望DP. f[i][j]表示i张红牌,j张黑牌的期望. i=0时,f[0][j]=0. j=0时,f[i][0]=i. f[i][j]=max ...
- 基本控件文档-UIButton属性---iOS-Apple苹果官方文档翻译
本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址 //转载请注明出处--本文永久链接:http://www.cnblogs.com/Ch ...
- 【HNOI】 期望面积
[题目描述]给定n个点,求这n个点组成凸包的期望面积.保证任意三点不共线. [数据范围]n<=100. 首先我们知道凸包面积的计算为所有在凸包上相邻的点的叉积和,那么我们可以枚举两个点,然后求出 ...
- sublime3 快捷键大全
Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件Ctrl+Shift+V:粘贴并格 ...
- Django【进阶】分页功能Pagination
项目中,我们需要很多非业务逻辑的功能,例如分页功能,而且此类功能移植性很好,可以在不同的项目中使用,所以整理好这些功能会一定程度上提高开发效率,下面是分页功能代码,使用时,可单独放在utils目录 & ...
- JAVA常见的集合类
关系的介绍: Set(集):集合中的元素不按特定方式排序,并且没有重复对象.他的有些实现类能对集合中的对象按特定方式排序. List(列表):集合中的元素按索引位置排序,可以有重复对象,允许按照对象在 ...
- 2.Python3标准库--文本
(一)string:文本常量和模板 1.函数 import string ''' string模块在最早的Python版本中就已经有了.以前这个模块中提供的很多函数已经移植到str对象中,不过这个模块 ...