在做一个项目的时候,遇到存放上传图片的问题,一开始是存放到文件价的,后来发现管理起来不怎么方便,后来就想到存放到数据库;于是就开始实现,可是发现遇到了很多的问题;
好的,下面我就对我对问题的解决做一个记录:
开发环境:MyJSF, MySQL, Hibernate, 用MyEclipse开发;
要解决的问题:把上传的图片进行压缩,并存放到数据库;
相关知识的了解:
存放图片的数据库字段为: Blob(最大的长度为65k)
我的数据库表为:
CREATE TABLE PhotoInfo
(
PhotoInfoID int unsigned auto_increment,
name char(35),
type char(15),
width int,
height int,
image blob,
primary key(`PhotoInfoID`)
);
图片的压缩算法为(下面的可以放到工具类中,我是放在Util中):
/*
* 压缩图片,并返回一个byte数组
*/
public static byte[] ScaleImage(InputStream input, double maxHeight,
double maxWidth) throws IOException {
// 压缩图片
BufferedImage srcImage = ImageIO.read(input);
double Ratio = 1.0;
if (srcImage.getHeight() > maxHeight || srcImage.getWidth() > maxWidth) {
if (srcImage.getHeight() > srcImage.getWidth())
Ratio = maxWidth / srcImage.getWidth();
else
Ratio = maxHeight / srcImage.getHeight();
}
AffineTransformOp op = new AffineTransformOp(AffineTransform
.getScaleInstance(Ratio, Ratio), null);
BufferedImage image = op.filter(srcImage, null);
;
return bufferedImageToByteArray(image);
}
/*
* 把图片转换成byte类型,主要用于图片上传后存放到数据库中的
*/
public static byte[] bufferedImageToByteArray(BufferedImage img)
throws ImageFormatException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
encoder.encode(img);
return os.toByteArray();
}
/*
* 得到文件的MINE Type
*/
public static String getMimeType(String filename) {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
return fileNameMap.getContentTypeFor(filename);
}
/*
* 对压缩算法的测试:
*/
public static void main(String[] args) throws IOException {
String inUrl = "", outUrl = ""; //please give your url
int width = 140, height = 140;
FileInputStream fin = new FileInputStream(inUrl);
FileOutputStream fout = new FileOutputStream(outUrl);
byte[] bytes = ScaleImage(fin, 140, 140);
String str = new String(bytes);
fout.write(bytes);
System.out.println(getMimeType(outUrl));
}
/*
* 图片上传代码片段;commons-fileupload
* 在jsf中,这个函数可以被上传按钮作为action属性
* action="#{xx.upload}"
*/
private UploadedFile _upFile;
private String _name = "";
public String upload() throws IOException {
String uploadedFileType = _upFile.getContentType();
if (uploadedFileType.equals("image/jpeg")
|| uploadedFileType.equals("image/pjpeg")
|| uploadedFileType.equals("image/gif")
|| uploadedFileType.equals("image/png")
|| uploadedFileType.equals("image/x-png")) {
//Photoinfo 是用Hibernate反向工程
//得到的Bean类(相应的数据库字段参加上面)
Photoinfo pif = Util.getSM().getPhotoInfo();
pif.setType(uploadedFileType);
byte[] bytes = Util.ScaleImage(_upFile.getInputStream(),
pif.getWidth(), pif.getHeight());
pif.setImage(bytes);
} else {
}
return "ok";
}
public boolean isUploaded() {
return Util.getSM().getPhotoInfo().getImage() != null;
}
在fileupload_showing.jsp中做相应的设置:
Photoinfo pif = Util.getSM().getPhotoInfo();
String contentType = pif.getType();
String fileName = pif.getName();
response.setContentType(contentType);
byte[] bytes = pif.getImage();
if (bytes != null)
{
response.getOutputStream().write(bytes);
}
jsp页面上的设置:
<h:panelGrid columns="1" rendered="#{Photo_Backing.uploaded}">
<h:graphicImage url="../fileupload_showimg.jsp"/>
</h:panelGrid>
<t:inputFileUpload id="fileuploadsss"
accept="image/*"
value="#{Photo_Backing.upFile}"
storage="file"
styleClass="fileUploadInput"
maxlength="200000"/>
<h:commandButton value="上传" action="#{Photo_Backing.upload}" />
最后就是对Hibernate中的blob进行设置(参考http://www.hibernate.org/73.html):
Hibernate 1.2.3 has built-in support for blobs. Hibernate natively maps blob columns to java.sql.Blob. However, it's sometimes useful to read the whole blob into memory and deal with it as a byte array.
One approach for doing this to create a new UserType as follows.
package mypackage;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.sql.Blob;
import cirrus.hibernate.Hibernate;
import cirrus.hibernate.HibernateException;
import cirrus.hibernate.UserType;
public class BinaryBlobType implements UserType
{
public int[] sqlTypes()
{
return new int[] { Types.BLOB };
}
public Class returnedClass()
{
return byte[].class;
}
public boolean equals(Object x, Object y)
{
return (x == y)
|| (x != null
&& y != null
&& java.util.Arrays.equals((byte[]) x, (byte[]) y));
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException
{
Blob blob = rs.getBlob(names[0]);
return blob.getBytes(1, (int) blob.length());
}
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException
{
st.setBlob(index, Hibernate.createBlob((byte[]) value));
}
public Object deepCopy(Object value)
{
if (value == null) return null;
byte[] bytes = (byte[]) value;
byte[] result = new byte[bytes.length];
System.arraycopy(bytes, 0, result, 0, bytes.length);
return result;
}
public boolean isMutable()
{
return true;
}
}
The BinaryBlobType will convert a blob into a byte array and back again.
Here's how to use it. First, define an entity that contains a byte[] property:
public class ImageValue
{
private long id;
private image byte[];
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public byte[] getImage() { return image; }
public void setImage(byte[] image) { this.image = image; }
}
Then map a blob column onto the byte[] property:
<class name="ImageValue" table="IMAGE_VALUE">
<id name="id/>
<property name="image" column="IMAGE" type="mypackage.BinaryBlobType"/>
</class>
Notes:
1) Blobs aren't cachable. By converting the blob into a byte array, you can now cache the entity.
2) This approach reads the whole blob into memory at once.
3) The above type is known to work for reading blobs out of the db. Other usage patterns might also work.
参考资料: