我最近在做一个宠物网站, 主打产品是“狗狗”。其中我在添加狗狗时需要添加狗狗的照片, 刚开始接触时觉得这似乎不成什么问题, 因为之前有做过将图片直接插入数据库的练习, 在oracle中将图片相应的字段设置为blob然后再进行流的读写即可。但是当开始真正做项目时才意识到了这真是一个难题, 到现在我还没有试验成功, 将整个过程记录一下:
1、 首先考虑直接往数据库中插入图片,参考了很多资料前后忙了将近两个小时 后来放弃了, 一则是这样子操作较复杂,而也是太占数据库容量了读取数据时影响速度。
2、 联想到自己前几天用JS实现的一个关于“图片预览”的东东,突然觉得保存图片的存储路径即可以了, 取图片时取得图片对应的路径然后再将路径赋给图片的src去实现。 接着便马上开始试验,试验成功,正当为自己的灵机一动兴奋时突然又想到了自己只是在本机上实现了, 我并没有考虑将图片存进服务器。
3、 关于如何上传图片到服务器于我而言又是一个难题, 之前不知有组件这种方式,上网搜了很多,感觉那些解决方案都较难。无意中发现一个帖子说可以用组件来实现, 于是便立马从网上下载了一个jsp smartupload组件来使用。 参考这示例代码上传照片成功了, ^_^ ^_^ 正当再次欢呼时又面临新的问题了, 因为我上传图片使用的是
<FORM ENCTYPE="multipart/form-data" action="get.jsp">
<input type="hidden" value="aaa">
</form>这种形式的, 这样子我在get.jsp页面中就取不到hidden表单中的值了; 而当我将ENCTYPE="multipart/form-data"去掉时虽然能够取得hidden文本域的值但又实现不了上传....
晕, 郁闷,虽然我将上传于获取普通文本域的值分成两个form表单来做是可以的, 但是我就想放一个表单里来做该怎么办呀? ————我为了不拖延项目完工的时间只好做成两个表单。
———先记录一下网上搜索到的相关资料:
a、 BinaryRead 方法用于读取作为 POST 请求的一部分从客户端发出的未加工数据。此方法用于在底层访问数据,与此相反,Request.Form 集合用于查看在公告请求中发送的表格数据。一旦调用了 BinaryRead,则引用 Request.Form 集合中的任何变量都将导致错误发生。反之,一旦引用了 Request.Form 集合中的一个变量,则调用 BinaryWrite 也将导致错误发生。请记住,如果在访问 Request 集合中的变量时未指定该变量属于哪一个子集,将搜索 Request.Form 集合并强制使用上述规则。
那么,如何在表单的 enctype 属性为 multipart/form-data 的情况下取得表单元素值呢?思路很简单:BinaryRead方法对当前输入流进行指定字节数的二进制读取 -> 借助 RecordSet 的 AppendChunk 方法将二进制流转化成文本 -> 用正则分析出文本中包含的元素名及元素值
Dim binData, intSize, objFormValue
intSize = Request.TotalBytes
binData = Request.BinaryRead(intSize)
Set objFormValue = FormValue(binData, intSize)
Const adLongVarChar = 201
Dim RS, strData, strTmp, objRegExp, objDictReg, objMatches, Match
Set RS = CreateObject("ADODB.Recordset")
RS.Fields.Append "mBinary", adLongVarChar, intSize
RS.Open
RS.AddNew
RS("mBinary").AppendChunk(binData)
RS.Update
strData = RS("mBinary").Value
RS.Close
Set RS = Nothing
现在 strData 已经是包含表单值的字符串了
b、 同时有上传和一般输入,enctype也是multipart/form-data,所以这里是对的,问题在于server端如何处理请求。
主要是接收页面的处理.贴个例子给你看看
我的代码也贴一下
try
{
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
String server_path=request.getRealPath("/");
String saveDirectory =server_path+"/file_pic/";
String tmpDirectory = "c://";
int maxPostSize = 1024 * 1024;
boolean is_legal_file=false;
String FileDescription = null;
String FileName = null;
long FileSize = 0;
String ContentType = null;
int count = 0 ;
java.util.Vector text = new java.util.Vector();//存放非文件域信息
java.util.Vector vector_file = new java.util.Vector();//存放文件域信息
DiskFileUpload upload = new DiskFileUpload();//初始化上传组件
upload.setSizeThreshold(4096);//设置文件流大小
upload.setSizeMax(maxPostSize);//设置最大文件,超过则临时存放到:tmpDirectory
upload.setRepositoryPath(tmpDirectory);//设置临时存放点
List items = upload.parseRequest(request);//取得request
Iterator iter = items.iterator();//初始化
int file_no=0;
int tmp = 0;
FileItem tmpItem = null;
//-------------------------------------------------
while (iter.hasNext())//表单有多个域
//-------------------------------------------------
{
tmp++;
//-------------------------------------------------
FileItem item = (FileItem) iter.next();
//-------------------------------------------------
if (item.isFormField())//如果是非文件域信息
{
FileDescription = item.getString();//取得非文件域信息,皆为字符串类型
text.addElement(FileDescription);//加入到存放非文件域信息之向量
}
else //如果是文件域信息
{
file_no++;
FileName = item.getName();
try //因为不同的浏览器会造成 path + filename, 有些则只有 filename
{
// for wintel platform
FileName = FileName.substring(FileName.lastIndexOf("//")+1);//取得档案名称
// for unix-like platform
FileName = FileName.substring(FileName.lastIndexOf("/")+1);//取得档案名称
}
catch (Exception ex)
{
System.out.println("取文件名出错:"+ex);
}
ContentType = item.getContentType();//取得档案类型
FileSize = item.getSize();//取得档案大小
tmpItem = item;
try // 將檔案寫入存檔目錄
{
//转换文件名
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyyMMddhhmmss");
java.util.Date currentTime_1 = new java.util.Date();
String ss=formatter.format(currentTime_1);
String FileName_houzhui=FileName.substring(FileName.lastIndexOf("."));
FileName=ss+file_no+FileName_houzhui;
vector_file.addElement(FileName);
File uploadedFile = new File(saveDirectory + FileName);
tmpItem.write(uploadedFile);
}
catch(Exception ex)
{
System.out.println("保存文件出错:"+ex);
System.out.println("错误位置:/servlet/com.sxztb.upload.uploadServlet");
}
}
}
//转到写文件类,完成写文件
try
{
upload_write_file_servlet tody_log = new upload_write_file_servlet(server_path);
tody_log.log_wite((String)text.elementAt(0),(String)text.elementAt(1),(String)text.elementAt(2),(String)text.elementAt(3),(String)vector_file.elementAt(0),(String)vector_file.elementAt(1),(String)vector_file.elementAt(2));
}
catch (Exception e)
{
System.out.println("生成JSP文件出现错误!"+e);
System.out.println("错误位置:/servlet/com.sxztb.upload.uploadServlet");
}
//写文件结束,转到相应目录
getServletContext().getRequestDispatcher("/news/").forward(request,response);
}
catch(Exception e)
{
System.out.println("/servlet/com.sxztb.upload.uploadServlet出错"+e);
}
*******************************************************************************************************************
上述的取得request中的表单中的每一个元素,可以不用迭代器 iterator.
List类中的List.size()取得多少个元素,
List类中的List.get(i)取得某个元素,
以上的这几行代码可以这样改!:
-------------------------------------------------
while (iter.hasNext())//表单有多个域
-------------------------------------------------
改:
for(int i=0;i<items.size();i++) //items是List对象
-------------------------------------------------
FileItem item = (FileItem) iter.next();
-------------------------------------------------
改
FileItem item = (FileItem) items.get(i);
c、
我一般是这样做的先在申明一个处理上传文件的javaBean
<jsp:useBean id="myUpload" scope="page" class="xiaoxiang.fileUpload.upBean" />
然后通过下面的方法可取另外的参数值
myUpload.getRequest().getParameter("参数名或控件名")
这个方法绝对OK
d、 还得靠自己,总算找到办法了,用下面方法可以取道文本等控件的值,只不过执行下面代码后request变量里面为空了(其值付给了List),如果取file控件的值的操作在后面(在同一地点就没有这种问题了)就不能用request了,而应该直接用List变量items 或 Iterator变量itr
List items = null;
try{
DiskFileUpload upload = new DiskFileUpload();
items = upload.parseRequest(request);
Iterator itr = items.iterator();
while(itr.hasNext()) {
FileItem item = (FileItem) itr.next();
if(item.isFormField()) {
String fieldName = item.getFieldName();
if(fieldName.equals("txtContentsJ")){
//内容(日本版)
reqContentsJ = item.getString();
}else if(fieldName.equals("txtContentsE")){
//内容(英語版)
reqContentsE = item.getString();
}
}
}
}catch(Exception e) {
logger.error("Exception:" + e);
}
4、 3.c的方法既简单又可行, 但是当解决了此问题时又引发了一个新的问题那就是: 由于图片都是由客户端上传到服务器的, 那么我如何保证即使上传了同名的照片而以前的照片不被覆盖呢?
我想了一下, 觉得应该将这个存入服务器图片的路径与某项带有唯一性质的属性进行绑定。 上网搜索了一下: 用数据库记录下文件名,但保存的时候是系统生成的文件名,如表的ID,这样就实现了你的要求。显示的时候用数据库的文件名,替代实际的文件名。
^_^ ^_^ 我也是这么想的, 现在正打算去试验。
———— 补充之 ENCTYPE="multipart/form-data"什么意思???
表单元素中的 enctype 属性规定了传递给服务器的表数据集编码的内容类型。enctype 属性的默认值是“application/x-www-form-urlencoded”,但当向服务器传送大量文本、包含非ASCII字符或二进制数的数据时,这个默认类型就不能胜任hq了。这时,文件上载提交表单时应使用“multipart/form-data”内容类型。 也即以流的方式上传数据,用于上传文件数据。
ENCTYPE不是单独设置的属性,它与method配饰使用,只有当method时post时ENCTYPE的设置才显得有意义。
————什么是MIME格式: Multipurpose Internet Mail Extensions
简称 MIME
多用途互联网络电子邮件格式的扩展。
用MIME关键字来区分文件编码格式文件种类(包括多媒体)。