• Java实现下载BLOB字段中的文件


    概述

    web项目的文件下载实现;servlet接收请求,spring工具类访问数据库及简化大字段内容获取。

    虽然文章的demo中是以sevlet为平台,想必在spring mvc中也有参考意义。

    核心代码

    响应设置和输出

     1 public void service(ServletRequest request, ServletResponse response)
     2      throws ServletException, IOException {
     3   /* 1. 设置响应内容类型 */
     4   response.setContentType("Application/Octet-stream;charset=utf-8");
     5    
     6   /* 2. 读取文件 */
     7   String fileName = ... // 获取文件名
     8   fileName = new String(file.getName().getBytes(), "ISO-8859-1");
     9   InputStream is = ... // 获取文件流
    10    
    11   /* 3. 将文件名加入响应头 */
    12   String cd = "attachment; filename=${fileName}"
    13      .replaceFirst("\$\{fileName\}", fileName);
    14   ((HttpServletResponse) response).addHeader("Content-Disposition", cd);
    15    
    16   /* 4. 将内容写到指定输出流,设置响应内容长度 */
    17   OutputStream os = response.getOutputStream();
    18   int length = org.springframework.util.FileCopyUtils.copy(is, os);
    19   response.setContentLength(length); // 不设置长度也可
    20    
    21   /* 5. 关闭输出流 */
    22   os.close();
    23 }
    Servlet

    我们定义一个servlet用于接收文件下载的请求,按照上述代码实现文件下载服务。但是我们遗留了两个问题:

    1. 如何获取文件名
    2. 如何获取文件的输入流

    虽然这两个问题并非难解,我们依然提供一个参考;考虑到文件可能存放在文件服务器或者数据库等多种形式,这里仅提供基于数据库的获取方案。

    获取文件名和文件内容

     1 /* 创建JdbcTemplate用以查询数据 */
     2 org.springframework.jdbc.core.JdbcTemplate jt = new org.springframework.jdbc.core.JdbcTemplate(ds);
     3     // 以java.sql.DataSource实例作为参数
     4   
     5 /* 创建LobHandler用以简化Lob字段读取 */
     6 // 在内部对象的方法中使用,需声明为final
     7 final org.springframework.jdbc.support.lob.LobHandler lobHandler = new org.springframework.jdbc.support.lob.DefaultLobHandler();
     8   
     9 /* 创建Map用来存放文件名和文件内容 */
    10 final Map file = new HashMap();
    11   
    12 /* 确保可以查询到数据记录 - 取第一条 */
    13 jt.query(
    14      sql,
    15      args,
    16      new org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor() {
    17   
    18        protected void streamData(ResultSet rs)
    19             throws SQLException, IOException,
    20             DataAccessException {
    21           // 注意,没有调用过rs.next();rs初始化已指向第一条记录
    22           String fileName = rs.getString("filename");
    23           InputStream is = lobHandler.getBlobAsBinaryStream(rs, "filecontent");
    24           file.put("filename", fileName);
    25           file.put("filecontent", is);
    26        }
    27      });
    基于spring JdbcTemplate获取文件名和文件流

    上面一段代码可以用来获取存在数据库的文件名和文件内容(BLOB字段):

    1. JdbcTemplate用来访问数据库
      依赖于数据源实例。
    2. LobHandler用于简化Lob字段的读取
      代码只是展示了针对BLOB字段的一种用法,关于CLOB的或其他方法,请查阅API。
    3. 匿名内部类
      我们定义了基于AbstractLobStreamingResultSetExtractor的匿名内部类,并创建了对象实例。实例的方法中所访问的实例外的变量,要求必须是final类型的,所以我们把LobHandler定义成final的。鉴于同样的原因,我们无法直接把实例内部查询到的文件名和文件内容,直接赋值给外部的变量(因为外部的必须是final的),所以我们定义了一个final Map对象,用来存放结果。

    代码优化

    包图

    包说明:

    1. cn.com.hnisi.fzyw.xzfy.gz.test.servlet
      集中管理servlet
    2. cn.com.hnisi.fzyw.xzfy.gz.download.service
      集中管理处理请求的service,及创建service实例的工厂
    3. cn.com.hnisi.fzyw.xzfy.gz.download.domain
      集中管理pojo
    4. cn.com.hnisi.baseservices.db
      提供数据库访问功能

    类图

    数据库访问支持组件

    DataSourceFactory

    DataSource工厂,负责向JdbcTemplateFactory提供可用的DataSource实例。

     1 package cn.com.hnisi.baseservices.db;
     2   
     3 import javax.naming.InitialContext;
     4 import javax.naming.NamingException;
     5 import javax.sql.DataSource;
     6   
     7 import cn.com.hnisi.baseservices.config.Config;
     8 /**
     9  * SINOBEST 数据源工厂,用于获取DataSource.<br>
    10  * 使用JNDI查找上下文中的DataSource.<br>
    11  * @author lijinlong
    12  *
    13  */
    14 public class DataSourceFactory {
    15   private static DataSourceFactory instance;
    16   private static InitialContext context;
    17   /** 默认的数据源的jndi名称属性的key */
    18   private static final String DEFAULT_DATASOURCE_PROP_KEY = "DB.DATASOURCE";
    19    
    20   private DataSourceFactory() {
    21      super();
    22   }
    23    
    24   /**
    25    * 单例模式获取工厂实例.<br>
    26    * @return
    27    */
    28   public static final synchronized DataSourceFactory newInstance() {
    29      if (instance == null)
    30        instance = new DataSourceFactory();
    31      return instance;
    32   }
    33    
    34   /**
    35    * 获取默认的DataSource.<br>
    36    * 根据config/config.properties中DB.DATASOURCE属性值查找.<br>
    37    * @return
    38    */
    39   public DataSource getDefaultDataSource() {
    40      String jndiName = Config.getInstance().getValue(DEFAULT_DATASOURCE_PROP_KEY);
    41      DataSource ds = getDataSource(jndiName);
    42      return ds;
    43   }
    44    
    45   /**
    46    * 根据JNDI名称,获取上下文中的DataSource.<br>
    47    * @param jndiName
    48    * @return
    49    */
    50   public synchronized DataSource getDataSource(String jndiName) {
    51      DataSource ds = null;
    52      try {
    53        if (context == null)
    54           context = new InitialContext();
    55        ds = (DataSource)context.lookup(jndiName);
    56      } catch (NamingException e) {
    57        e.printStackTrace();
    58      }
    59      return ds;
    60   }
    61 }
    DataSourceFactory
    JdbcTemplateFactory

    JdbcTemplate工厂,负责创建可用的JdbcTemplate实例。

     1 package cn.com.hnisi.baseservices.db;
     2   
     3 import javax.sql.DataSource;
     4   
     5 import org.springframework.jdbc.core.JdbcTemplate;
     6 /**
     7  * 用于获取{@link JdbcTemplate}实例的工厂.<br>
     8  * <ol>
     9  *   <li>使用{@link #getDefaultJT()}可以获取基于默认数据源({@link DataSourceFactory#getDefaultDataSource()})的JT实例.
    10  *  </li>
    11  *  <li>使用{@link #getJT(DataSource)}可以获取基于指定数据源的JT实例.
    12  *  </li>
    13  *  <li>使用{@link #getJT(String)}可以获取基于指定JNDI name的数据源的JT实例.
    14  *  </li>
    15  *  <li>
    16  *   其他的可能以后会补充.
    17  *  </li>
    18  * </ol>
    19  * @author lijinlong
    20  *
    21  */
    22 public class JdbcTemplateFactory {
    23   private static JdbcTemplateFactory instance;
    24   
    25   private JdbcTemplateFactory() {
    26      super();
    27   }
    28    
    29   public static synchronized JdbcTemplateFactory newInstance() {
    30      if (instance == null)
    31        instance = new JdbcTemplateFactory();
    32       
    33      return instance;
    34   }
    35    
    36   /**
    37    * 使用默认的数据源,获取{@link JdbcTemplate}对象实例.<br>
    38    * @return
    39    */
    40   public JdbcTemplate getDefaultJT() {
    41      DataSource ds = DataSourceFactory.newInstance().getDefaultDataSource();
    42      JdbcTemplate jt = getJT(ds);
    43      return jt;
    44   }
    45    
    46   /**
    47    * 使用指定的数据源,获取{@link JdbcTemplate}对象实例.<br>
    48    * @param ds
    49    * @return
    50    */
    51   public JdbcTemplate getJT(DataSource ds) {
    52      if (ds == null)
    53        return null;
    54       JdbcTemplate jt = new JdbcTemplate(ds);
    55       return jt;
    56   }
    57    
    58   /**
    59    * 根据数据源的jndiName,构造{@link JdbcTemplate}对象实例.
    60    * @param dsJndiName
    61    * @return
    62    */
    63   public JdbcTemplate getJT(String dsJndiName) {
    64      if (dsJndiName == null || dsJndiName.length() == 0)
    65        return null;
    66       
    67      DataSource ds = DataSourceFactory.newInstance().getDataSource(dsJndiName);
    68      JdbcTemplate jt = getJT(ds);
    69      return jt;
    70   }
    71 }
    JdbcTemplateFactory

    File组件

    自定义pojo,存放文件的name和content。

     1 package cn.com.hnisi.fzyw.xzfy.gz.download.domain;
     2   
     3 import java.io.InputStream;
     4   
     5 public class File {
     6   private String name;
     7   private InputStream is;
     8   public File() {
     9      super();
    10   }
    11   public File(String name, InputStream is) {
    12      super();
    13      this.name = name;
    14      this.is = is;
    15   }
    16   public String getName() {
    17      return name;
    18   }
    19   public void setName(String name) {
    20      this.name = name;
    21   }
    22   public InputStream getIs() {
    23      return is;
    24   }
    25   public void setIs(InputStream is) {
    26      this.is = is;
    27   }
    28 }
    File

    数据库访问组件

    IDownloadService

    文件下载服务接口。

     1 package cn.com.hnisi.fzyw.xzfy.gz.download.service;
     2   
     3 import java.util.List;
     4   
     5 import cn.com.hnisi.fzyw.xzfy.gz.download.domain.File;
     6   
     7 public interface IDownloadService {
     8   /**
     9    * 读取文件.<br>
    10    * 
    11    * @param sql
    12    *            查询sql语句,必须包含文件名字段和文件内容字段.
    13    * @param args
    14    *            参数 - 确保sql查询的结果只有一条.
    15    * @param colNameFileName
    16    *            存放文件名的字段名,大写.
    17    * @param colNameFileContent
    18    *            存放文件内容的字段名,大写.
    19    * @return
    20    */
    21   public File read(final String sql, final Object[] args,
    22        final String colNameFileName, final String colNameFileContent);
    23 }
    IDownloadService
    DownloadServiceImpl

    文件下载服务实现类。

     1 package cn.com.hnisi.fzyw.xzfy.gz.download.service;
     2   
     3 import java.io.IOException;
     4 import java.sql.ResultSet;
     5 import java.sql.SQLException;
     6   
     7 import org.springframework.dao.DataAccessException;
     8 import org.springframework.jdbc.core.JdbcTemplate;
     9 import org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor;
    10 import org.springframework.jdbc.support.lob.DefaultLobHandler;
    11 import org.springframework.jdbc.support.lob.LobHandler;
    12   
    13 import cn.com.hnisi.baseservices.db.JdbcTemplateFactory;
    14 import cn.com.hnisi.fzyw.xzfy.gz.download.domain.File;
    15   
    16 public class DownloadServiceImpl implements IDownloadService {
    17   /**
    18    * SINOBEST common 文件下载实现.
    19    */
    20   public File read(final String sql, final Object[] args,
    21        final String colNameFileName, final String colNameFileContent) {
    22      JdbcTemplate jt = JdbcTemplateFactory.newInstance().getDefaultJT();
    23      final LobHandler lobHandler = new DefaultLobHandler();
    24      final File file = new File();
    25      jt.query(sql, args, new AbstractLobStreamingResultSetExtractor() {
    26   
    27        protected void streamData(ResultSet rs) throws SQLException,
    28             IOException, DataAccessException {
    29           file.setName(rs.getString(colNameFileName));
    30           file.setIs(lobHandler.getBlobAsBinaryStream(rs,
    31                colNameFileContent));
    32        }
    33      });
    34      return file;
    35   }
    36   
    37 }
    DownloadServiceImpl
    DownloadServiceFactory

    文件下载服务工厂,创建服务组件实例。

     1 package cn.com.hnisi.fzyw.xzfy.gz.download.service;
     2   
     3 public class DownloadServiceFactory {
     4   private static DownloadServiceFactory ds;
     5   
     6   private DownloadServiceFactory() {
     7      super();
     8   }
     9    
    10   public static synchronized DownloadServiceFactory newInstance() {
    11      if (ds == null)
    12        ds = new DownloadServiceFactory();
    13      return ds;
    14   }
    15    
    16   public IDownloadService createDownloadService() {
    17      IDownloadService ds = new DownloadServiceImpl();
    18      return ds;
    19   }
    20 }
    DownloadServiceFactory

    Servlet

    FileDownloadTestServlet

    文件下载请求的servlet

     1 package cn.com.hnisi.fzyw.xzfy.gz.test.servlet;
     2   
     3 import java.io.IOException;
     4 import java.io.OutputStream;
     5   
     6 import javax.servlet.ServletException;
     7 import javax.servlet.ServletRequest;
     8 import javax.servlet.ServletResponse;
     9 import javax.servlet.http.HttpServlet;
    10 import javax.servlet.http.HttpServletResponse;
    11   
    12 import org.springframework.util.FileCopyUtils;
    13   
    14 import cn.com.hnisi.fzyw.xzfy.gz.download.domain.File;
    15 import cn.com.hnisi.fzyw.xzfy.gz.download.service.DownloadServiceFactory;
    16   
    17 public class FileDownloadTestServlet extends HttpServlet {
    18   
    19   private static final long serialVersionUID = -2168892287436159079L;
    20   /**
    21    * SINOBEST common 文件下载测试.
    22    */
    23   public void service(ServletRequest request, ServletResponse response)
    24        throws ServletException, IOException {
    25      /* 1. 设置响应内容类型 */
    26      response.setContentType("Application/Octet-stream;charset=utf-8");
    27       
    28      /* 2. 读取文件 */
    29      String sql = "select CLMC, SQCL from V_FZYWGZ_JK_XZFYSQXX_CL where SYSTEMID=?";
    30      String systemid = request.getParameter("systemid");
    31      Object[] args = { systemid };
    32      String colNameFileName = "CLMC";
    33      String colNameFileContent = "SQCL";
    34      File file = DownloadServiceFactory.newInstance()
    35           .createDownloadService()
    36           .read(sql, args, colNameFileName, colNameFileContent);
    37       
    38      /* 3. 将文件名加入响应头 */
    39      String fileName = new String(file.getName().getBytes(), "ISO-8859-1");
    40      ((HttpServletResponse) response).addHeader("Content-Disposition",
    41           "attachment; filename=" + fileName);
    42       
    43      /* 4. 将内容写到指定输出流,设置响应内容长度 */
    44      OutputStream os = response.getOutputStream();
    45      int length = FileCopyUtils.copy(file.getIs(), os);
    46      response.setContentLength(length);
    47       
    48      /* 5. 关闭输出流 */
    49      os.close();
    50   }
    51 }
    FileDownloadTestServlet
  • 相关阅读:
    SQL错误码
    用collectionview实现瀑布流-转
    iOS开发~CocoaPods使用详细说明
    传值
    iOS 传值方式
    iOS Cocos2d-x 开发环境的搭建
    iOS 高效率编程工具篇
    iOS地图定位系统语言为英文时,返回中文位置信息
    Xcode脚本自动化打包问题:xcrun: error: unable to find utility "PackageApplication", not a developer tool or in PATH
    Cannot create __weak reference in file xxxxxxxxx
  • 原文地址:https://www.cnblogs.com/ywjy/p/4992578.html
Copyright © 2020-2023  润新知