Java實現下載BLOB字段中的文件

概述

web項目的文件下載實現;servlet接收請求,spring工具類訪問數據庫及簡化大字段內容獲取。java

雖然文章的demo中是以sevlet爲平臺,想必在spring mvc中也有參考意義。web

核心代碼

響應設置和輸出

 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用於接收文件下載的請求,按照上述代碼實現文件下載服務。可是咱們遺留了兩個問題:spring

  1. 如何獲取文件名
  2. 如何獲取文件的輸入流

雖然這兩個問題並不是難解,咱們依然提供一個參考;考慮到文件可能存放在文件服務器或者數據庫等多種形式,這裏僅提供基於數據庫的獲取方案。sql

獲取文件名和文件內容

 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實例。mvc

 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實例。dom

 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。ide

 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
相關文章
相關標籤/搜索