13.5.2 处理Blob类型数据

13.5.2 处理Blob类型数据

Blob(Binary Long Object)是二进制长对象的意思,Blob列通常用于存储大文件,典型的Blob内容是一张图片或一个声音文件,由于它们的特殊性,必须使用特殊的方式来存储。使用Blob列可以把图片、声音等文件的二进制数据保存在数据库里,并可以从数据库里恢复指定文件。
如果需要将图片插入数据库,显然不能直接通过普通的SQL语句来完成,因为有一个关键的问题: Blob常量无法表示。所以将Blob数据插入数据库需要使用PreparedStatement,该对象有一个方法:setBinaryStream(int parameterIndex, InputStream x),该方法可以为指定参数传入二进制输入流,从而可以实现将Blob数据保存到数据库的功能。
当需要从ResultSet里取出Blob数据时,可以调用ResultSetgetBlob( int columnIndex)方法,该方法将返回一个Blob对象,Blob对象提供了getBinaryStream()方法来获取该Blob数据的输入流,也可以使用Blb对象提供的getBytes()方法直接取出该Blob对象封装的二进制数据。
为了把图片放入数据库,本程序先使用如下SQL语句来建立一个数据表。

1
2
3
4
5
6
create table img_table
(
img_id int auto_increment primary key,
img_name varchar(255),
img_data mediumblob
);

上面SQL语句中的img_data列使用mediumblob类型,而是blob类型。因为MySQL数据库里的blob类型最多只能存储64KB内容,这可能不够满足实际用途。所以使用mediumblob类型,该类型的数据列可以存储16MB内容.
下面程序可以实现图片”上传”——实际上就是将图片保存到数据库,并在右边的列表框中显示图片的名字,
当用户双击列表框中的图片名时,左边窗口将显示该图片—实质就是根据选中的ID从数据库里查找图片,并将其显示出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
import java.sql.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Properties;
import java.util.ArrayList;
import java.io.*;
import javax.swing.filechooser.FileFilter;

public class BlobTest
{
JFrame jf = new JFrame("图片管理程序");
private static Connection conn;
private static PreparedStatement insert;
private static PreparedStatement query;
private static PreparedStatement queryAll;
// 定义一个DefaultListModel对象
private DefaultListModel<ImageHolder> imageModel = new DefaultListModel<>();
private JList<ImageHolder> imageList = new JList<>(imageModel);
private JTextField filePath = new JTextField(26);
private JButton browserBn = new JButton("...");
private JButton uploadBn = new JButton("上传");
private JLabel imageLabel = new JLabel();
// 以当前路径创建文件选择器
JFileChooser chooser = new JFileChooser(".");
// 创建文件过滤器
ExtensionFileFilter filter = new ExtensionFileFilter();
static
{
try
{
Properties props = new Properties();
props.load(new FileInputStream("mysql.ini"));
String driver = props.getProperty("driver");
String url = props.getProperty("url");
String user = props.getProperty("user");
String pass = props.getProperty("pass");
// 1.加载数据库驱动
Class.forName(driver);
// 2.获取数据库连接
conn = DriverManager.getConnection(url, user, pass);
// 3.创建执行插入的PreparedStatement对象,
// 该对象执行插入后可以返回自动生成的主键
insert = conn.prepareStatement(
"insert into img_table" + " values(null,?,?)",
Statement.RETURN_GENERATED_KEYS);
// 创建两个PreparedStatement对象,用于查询指定图片,查询所有图片
query = conn.prepareStatement(
"select img_data from img_table" + " where img_id=?");
queryAll = conn.prepareStatement(
"select img_id, " + " img_name from img_table");
} catch (Exception e)
{
e.printStackTrace();
}
}
public void init() throws SQLException
{
// -------初始化文件选择器--------
filter.addExtension("jpg");
filter.addExtension("jpeg");
filter.addExtension("gif");
filter.addExtension("png");
filter.setDescription("图片文件(*.jpg,*.jpeg,*.gif,*.png)");
chooser.addChoosableFileFilter(filter);
// 禁止“文件类型”下拉列表中显示“所有文件”选项。
chooser.setAcceptAllFileFilterUsed(false);
// ---------初始化程序界面---------
fillListModel();
filePath.setEditable(false);
// 只能单选
imageList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JPanel jp = new JPanel();
jp.add(filePath);
jp.add(browserBn);
browserBn.addActionListener(event ->
{
// 显示文件对话框
int result = chooser.showDialog(jf, "浏览图片文件上传");
// 如果用户选择了APPROVE(赞同)按钮,即打开,保存等效按钮
if (result == JFileChooser.APPROVE_OPTION)
{
filePath.setText(chooser.getSelectedFile().getPath());
}
});
jp.add(uploadBn);
uploadBn.addActionListener(avt ->
{
// 如果上传文件的文本框有内容
if (filePath.getText().trim().length() > 0)
{
// 将指定文件保存到数据库
upload(filePath.getText());
// 清空文本框内容
filePath.setText("");
}
});
JPanel left = new JPanel();
left.setLayout(new BorderLayout());
left.add(new JScrollPane(imageLabel), BorderLayout.CENTER);
left.add(jp, BorderLayout.SOUTH);
jf.add(left);
imageList.setFixedCellWidth(160);
jf.add(new JScrollPane(imageList), BorderLayout.EAST);
imageList.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
// 如果鼠标双击
if (e.getClickCount() >= 2)
{
// 取出选中的List项
ImageHolder cur = (ImageHolder) imageList
.getSelectedValue();
try
{
// 显示选中项对应的Image
showImage(cur.getId());
} catch (SQLException sqle)
{
sqle.printStackTrace();
}
}
}
});
jf.setSize(620, 400);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
// ----------查找img_table填充ListModel----------
public void fillListModel() throws SQLException
{

try (
// 执行查询
ResultSet rs = queryAll.executeQuery())
{
// 先清除所有元素
imageModel.clear();
// 把查询的全部记录添加到ListModel中
while (rs.next())
{
imageModel.addElement(
new ImageHolder(rs.getInt(1), rs.getString(2)));
}
}
}
// ---------将指定图片放入数据库---------
public void upload(String fileName)
{
// 截取文件名
String imageName = fileName.substring(fileName.lastIndexOf('\\') + 1,
fileName.lastIndexOf('.'));
File f = new File(fileName);
try (InputStream is = new FileInputStream(f))
{
// 设置图片名参数
insert.setString(1, imageName);
// 设置二进制流参数
insert.setBinaryStream(2, is, (int) f.length());
int affect = insert.executeUpdate();
if (affect == 1)
{
// 重新更新ListModel,将会让JList显示最新的图片列表
fillListModel();
}
} catch (Exception e)
{
e.printStackTrace();
}
}
// ---------根据图片ID来显示图片----------
public void showImage(int id) throws SQLException
{
// 设置参数
query.setInt(1, id);
try (
// 执行查询
ResultSet rs = query.executeQuery())
{
if (rs.next())
{
// 取出Blob列
Blob imgBlob = rs.getBlob(1);
// 取出Blob列里的数据
ImageIcon icon = new ImageIcon(
imgBlob.getBytes(1L, (int) imgBlob.length()));
imageLabel.setIcon(icon);
}
}
}
public static void main(String[] args) throws SQLException
{
new BlobTest().init();
}
}
// 创建FileFilter的子类,用以实现文件过滤功能
class ExtensionFileFilter extends FileFilter
{
private String description = "";
private ArrayList<String> extensions = new ArrayList<>();
// 自定义方法,用于添加文件扩展名
public void addExtension(String extension)
{
if (!extension.startsWith("."))
{
extension = "." + extension;
extensions.add(extension.toLowerCase());
}
}
// 用于设置该文件过滤器的描述文本
public void setDescription(String aDescription)
{
description = aDescription;
}
// 继承FileFilter类必须实现的抽象方法,返回该文件过滤器的描述文本
public String getDescription()
{
return description;
}
// 继承FileFilter类必须实现的抽象方法,判断该文件过滤器是否接受该文件
public boolean accept(File f)
{
// 如果该文件是路径,接受该文件
if (f.isDirectory())
return true;
// 将文件名转为小写(全部转为小写后比较,用于忽略文件名大小写)
String name = f.getName().toLowerCase();
// 遍历所有可接受的扩展名,如果扩展名相同,该文件就可接受。
for (String extension : extensions)
{
if (name.endsWith(extension))
{
return true;
}
}
return false;
}
}
// 创建一个ImageHolder类,用于封装图片名、图片ID
class ImageHolder
{
// 封装图片的ID
private int id;
// 封装图片的图片名字
private String name;
public ImageHolder()
{
}
public ImageHolder(int id, String name)
{
this.id = id;
this.name = name;
}
// id的setter和getter方法
public void setId(int id)
{
this.id = id;
}
public int getId()
{
return this.id;
}
// name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
// 重写toString方法,返回图片名
public String toString()
{
return name;
}
}

下面是一个简单的查询执行器,当用户在文本框内输入合法的查询语句并执行成功后,下面的表格将会显示查询结果。