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" 要带双引号,网上很多不带的,我测试时出错,并且 ...
随机推荐
- 【转】 GRASP(通用职责分配软件模式)模式
转自:http://www.cnblogs.com/sevenyuan/archive/2010/03/05/1678730.html 及:http://blog.csdn.net/lovelion ...
- Sass 颜色函数
/* * Sass 颜色函数 * RGB 颜色函数 * 1. rgb($red,$green,$blue):根据红.绿.蓝三个值创建一个颜色: * rgb(200,40,88) //根据r:200,g ...
- 【Foreign】字串变化 [DP]
字串变化 Time Limit: 10 Sec Memory Limit: 128 MB Description 定义一个(大写字母)字符串集合{S},初始时值包含一个给定的字符串S1,每次从中任意 ...
- 【51NOD】消灭兔子
[算法]贪心 #include<cstdio> #include<algorithm> #include<cstring> #include<queue> ...
- iOS开发者两分钟学会用GitHub在Mac上托管代码的两种方法
原文发布者:http://blog.csdn.net/duxinfeng2010 在Mac上使用Xcode进行iOS-Apple苹果iPhone手机开发过程中少不了使用GitHub在Mac上托 ...
- 全面了解Nginx主要应用场景(数漫江湖)
前言 本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文本身也可能介绍的不完整,毕竟只是我个人使用过和了解到过得.所以还请见谅,同时欢迎留言交流 N ...
- Can you answer these queries?(HDU4027+势能线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027 题目: 题意:n个数,每次区间更新将其数值变成它的根号倍(向下取整),区间查询数值和. 思路:易 ...
- bzoj 1079 DP
比较容易看出来是DP,但是如果我们记录每一种颜色还剩多少种的话,消耗的转移的时间复杂度5^15,但是我们考虑到每一种颜色,如果数量相同的话,其实是等效的,所以我们用w[a][b][c][d][e][l ...
- oracle 的number数据类型
NUMBER类型细讲:Oracle number datatype 语法:NUMBER[(precision [, scale])]简称:precision --> p scale ...
- hdu 1879 继续畅通工程 (并查集+最小生成树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1879 继续畅通工程 Time Limit: 2000/1000 MS (Java/Others) ...