上传至数据库的参考代码
要求:
想了解文件上传/下载/显示的具体实现方法,并有以下一些要求:
1. 将文件上传到服务器以Blob对象保存到oracle, 直接读取输入流后写进数据库,
不要采取"上传本地文件"->"在服务器端产生文件"->"读文件流"->"写库"的方式. 要求在写进库之前,得到文件的名称,大小,在判断文件是否存在。
2. 用Servlet实现文件显示。
3. 从数据库读出Blob对象,生成流直接下载到客户端。不要使用
"读取blob对象"->"在服务器目录下生成下载文件"的方式. 最好在界面上
对文件链接可以"左键显示","右键保存下载".
4. 请不要使用jspsmartupload组件
望大虾们能给出解决的办法.
这样你需要了解浏览器端的编码方式,在传到服务器端时你才能解码。也才可以得到上传文件的相关信息。下面是别处取来的代码,一直没时间去看看,正好你可以研究一下,看是否有用。
package mshtang.fileUpload;
import java.io.*;
/**一个存放文件信息的类,包括文件的名称(String),
**字段名(String), Content-Type(String)和内容(byte[])
**还提供了一个直接将文件内容保存到一个文件的函数 void saveTo(File f)
**可以调用 类{@link ContentFactory}中的适当方法,生成该类的实例。
** @see ContentFactory
** @see ContentFactory#getFileParameter
** @see ContentFactory#getFileParameterValues
**/
public class FileHolder
{
String contentType;
byte[] buffer;
String fileName;
String parameterName;
FileHolder(byte[] buffer, String contentType, String fileName, String parameterName)
{
this.buffer = buffer;
this.contentType = contentType;
this.fileName = fileName;
this.parameterName = parameterName;
}
/**把文件的内容存到指定的文件中,
**<b>这个方法不会检查这个文件是否可写、是否已经存在。</b>
**@param file 目的文件
**@throws 在 I/O 操作中被抛出的 IOException
**/
public void saveTo(File file) throws IOException
{
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
out.write(buffer);
out.close();
}
/**把文件的内容存到指定的文件中,
**<b>这个方法不会检查这个文件是否可写、是否已经存在。</b>
**@param name 目的文件名
**@throws 在 I/O 操作中被抛出的 IOException
**/
public void saveTo(String name) throws IOException
{
saveTo(new File(name));
}
/**
**返回一个文件内容的字节数组
**@return 一个代表文件内容的字节数组
**/
public byte[] getBytes()
{
return buffer;
}
/**
**返回该文件在文件上载前在客户端的名称
**@return 该文件在文件上载前在客户端的名称
**/
public String getFileName()
{
return fileName;
}
/**
**返回该文件的 Content-Type
**@return 该文件的 Content-Type
**/
public String getContentType()
{
return contentType;
}
/**
**返回上载该文件时,Html 页面窗体中 file 控件的 name 属性
**@return 返回上载该文件时,Html 页面窗体中 file 控件的 name 属性
**/
public String getParameterName()
{
return parameterName;
}
}
package mshtang.fileUpload;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/**存放报文内容的类,提供类似于 ServletRequest 中的部分 get 方法
*你必须在 html 页面的窗体(form)中指定 enctype="multipart/form-data"。
*才可以正确的使用这个类。
**/
public class ContentFactory
{
private Hashtable values; //存放name=value,其中value存放在另一个类中
private Hashtable files; //存放文件内容的。
private ContentFactory(Hashtable values, Hashtable files)
{
this.values=values;
this.files=files;
}
public String getParameter(String name)
{
Vector v = (Vector)values.get(name);
if( v != null)
{
return (String)v.elementAt(0);
}
return null;
}
public Enumeration getParameterNames()
{
return values.keys();
}
public String[] getParameterValues(String name)
{
Vector v = (Vector)values.get(name);
if(v != null)
{
String[] result = new String[v.size()];
v.toArray(result);
return result;
}
return new String[0];
}
/**
*返回一个 FileHolder 实例,该实例包含了通过字段名为name的file控件上载的文件信息,
*如果不存在这个字段或者提交页面时,没有选择上载的文件,则返回 null。
* <p>如果 Html 页面中存在不止一个字段名为name的file控件,
* 返回值等于{@link #getFileParameterValues}中的第一个元素。
*
* @param name:一个<code>String</code>,对应于Html页面窗体中file控件
*的name 属性。
*
* @return返回:一个 FileHolder 实例,该实例包含了通过字段名为 name 的 file 控件上载的文件信息,
*如果不存在这个字段或者提交页面是,没有选择上载的文件,则返回 null。
*
* @see #getFileParameterValues
*
*/
public FileHolder getFileParameter(String name)
{
Vector v = (Vector)files.get(name);
if(v != null)
{
return (FileHolder)v.elementAt(0);
}
return null;
}
/**
* 返回一个 由 String 对象构成的 Enumeration ,包含了 Html 页面
*窗体中所有 file 控件的 name 属性。
*如果窗体中没有 file 控件,则返回一个空的 Enumeration
* @return 返回一个 由 String 对象构成的 Enumeration ,包含了 Html 页面
*窗体中所有 file 控件的 name 属性。
*如果窗体中没有 file 控件,则返回一个空的 Enumeration
*/
public Enumeration getFileParameterNames()
{
return files.keys();
}
/**
*返回一个 FileHolder 数组,该数组包含了所有通过字段名为 name 的 file 控件上载的文件信息,
*如果不存在这个字段或者提交页面时,没有选择任何上载的文件,则返回一个 零元素的数组(不是 null )。
* @param name 一个 <code>String</code> ,对应于 Html 页面窗体中 file 控件
*的name 属性。
*
* @return 返回一个 FileHolder 数组,该数组包含了所有通过字段名为 name 的 file 控件上载的文件信息,
*如果不存在这个字段或者提交页面时,没有选择任何上载的文件,则返回一个 零元素的数组(不是 null )。
*
* @see #getFileParameter
*/
public FileHolder[] getFileParameterValues(String name)
{
Vector v=(Vector)files.get(name);
if(v!=null)
{
FileHolder[] result=new FileHolder[v.size()];
v.toArray(result);
return result;
}
return new FileHolder[0];
}
//------------->Factory 部分
/**
**返回根据当前请求生成的一个 ContentFactory 实例
**@param request 提交的请求
**@return 返回根据当前请求生成的一个 ContentFactory 实例,如果 request
数据包的内容不是以 mutilpart/form-data 型编码,则返回 null。
**@throws ContentFactoryException 当提交的数据和文件太大时抛出,
**根据 Content-Length 判断,默认的许可值为 1024* 1024。
**/
public static ContentFactory getContentFactory(HttpServletRequest request) throws ContentFactoryException,IOException
{
// default maxLength is 1MB.
return getContentFactory(request, 1024*1024);
}
/**
**返回根据当前请求生成的一个 ContentFactory 实例
**@param request 提交的请求
**@param maxLength 数据包的最大长度,默认为1024*1024
**@return 返回根据当前请求生成的一个 ContentFactory 实例,如果 request
数据包的内容不是以 mutilpart/form-data 型编码,则返回 null。
**@throws ContentFactoryException 当提交的数据和文件太大时抛出,
**根据 Content-Length 判断,默认的许可值为 1024* 1024。
**/
public static ContentFactory getContentFactory(HttpServletRequest request, int maxLength) throws ContentFactoryException, IOException
{
Hashtable values = new Hashtable();
Hashtable files = new Hashtable();
String contentType = request.getContentType();
int contentLength = request.getContentLength();
if (contentLength > maxLength)
{
ContentFactoryException e=new ContentFactoryException("上传数据太多,请不要选择太大的文件");
throw e;
}
if(contentType == null || !contentType.startsWith("multipart/form-data"))
{
return null;
}
//get out the boudary from content-type
int start = contentType.indexOf("boundary=");
//这里应该
int boundaryLen = new String("boundary=").length();
String boundary = contentType.substring(start + boundaryLen);
boundary = "--" + boundary;
//用字节表示,以免 String 和 byte 数组的长度不一致
boundaryLen = bytesLen(boundary);
//把request 中的数据读入一个byte数组
byte buffer[] = new byte[contentLength];
int once = 0;
int total = 0;
DataInputStream in = new DataInputStream(request.getInputStream());
while((total < contentLength) && (once >= 0))
{
once = in.read(buffer, total, contentLength);
total += once;
}
//对buffer中的数据进行拆分
int pos1 = 0; //pos1 记录 在buffer 中下一个 boundary 的位置
//pos0,pos1 用于 subBytes 的两个参数
int pos0 = byteIndexOf(buffer, boundary, 0);//pos0 记录 boundary 的第一个字节在buffer 中的位置
do
{
pos0 += boundaryLen; //记录boundary后面第一个字节的下标
pos1 = byteIndexOf(buffer, boundary, pos0);
if(pos1==-1)
{
break;
}//
pos0 += 2;//考虑到boundary后面的 \r\n
parse(subBytes(buffer, pos0, pos1-2), values, files); //考虑到boundary后面的\r\n
pos0=pos1;
}while(true);
return new ContentFactory(values,files);
}
private static void parse(byte[] buffer, Hashtable values, Hashtable files)
{
/* this is a smiple to parse
[boundary]
Content-Disposition: form-data; name="file3"; filename="C:\Autoexec.bat"
Content-Type: application/octet-stream
@echo off
prompt $d $t [ $p ]$_$$
[boundary]
Content-Disposition: form-data; name="Submit"
Submit
[boundary]
*/
String[] tokens={"name=\"","\"; filename=\"", "\"\r\n","Content-Type: ","\r\n\r\n"};
// 0 1 2 3 4
int[] position=new int[tokens.length];
for (int i=0;i<tokens.length ;i++ )
{
position[i]=byteIndexOf(buffer,tokens[i],0);
}
if (position[1]>0 && position[1]<position[2])
{
//包含tokens 中的第二个元素,说明是个文件数据段
//1.得到字段名
String name =subBytesString(buffer,position[0]+bytesLen(tokens[0]),position[1]);
//2.得到文件名
String file= subBytesString(buffer,position[1]+bytesLen(tokens[1]),position[2]);
if (file.equals("")) return;
file=new File(file).getName(); //this is the way to get the name from a path string
//3.得到 Content-Type
String contentType=subBytesString(buffer,position[3]+bytesLen(tokens[3]),position[4]);
//4.得到文件内容
byte[] b=subBytes(buffer,position[4]+bytesLen(tokens[4]),buffer.length);
FileHolder f=new FileHolder(b,contentType,file,name);
Vector v=(Vector)files.get(name);
if (v==null)
{
v=new Vector();
}
if (!v.contains(f))
{
v.add(f);
}
files.put(name,v);
//同时将 name 属性和 file 属性作为普通字段,存入values;
v=(Vector)values.get(name);
if (v==null)
{
v=new Vector();
}
if (!v.contains(file))
{
v.add(file);
}
values.put(name,v);
}else
{
// String[] tokens={"name=\"","\"; filename=\"", "\"\r\n","Content-Type: ","\r\n\r\n"}
// index 0 1 2 3 4
//不包含tokens 中的第二个元素,说明是个 name/value 型的数据段
//所以没有tokens[1]和 tokens[3]
//name 在 tokens[0] 和 tokens[2] 之间
//value 在 tokens[4]之后
//1.得到name
String name =subBytesString(buffer,position[0]+bytesLen(tokens[0]),position[2]);
String value= subBytesString(buffer,position[4]+bytesLen(tokens[4]),buffer.length);
Vector v=(Vector)values.get(name);
if (v==null)
{
v=new Vector();
}
if (!v.contains(value))
{
v.add(value);
}
values.put(name,v);
}
}
/**字节数组中的 indexof 函数,与 String 类中的 indexOf类似
**@para source 源字节数组
**@para search 目标字符串
**@para start 搜索的起始点
**@return 如果找到,返回search的第一个字节在buffer中的下标,没有则返回-1
**/
private static int byteIndexOf (byte[] source,String search,int start)
{
return byteIndexOf(source,search.getBytes(),start);
}
/**字节数组中的 indexof 函数,与 String 类中的 indexOf类似
**@para source 源字节数组
**@para search 目标字节数组
**@para start 搜索的起始点
**@return 如果找到,返回search的第一个字节在buffer中的下标,没有则返回-1
**/
private static int byteIndexOf (byte[] source,byte[] search,int start)
{
int i;
if (search.length==0)
{
return 0;
}
int max=source.length-search.length;
if (max<0)
return -1;
if (start>max)
return -1;
if (start<0)
start=0;
// 在source中找到search的第一个元素
searchForFirst:
for (i=start;i<=max ; i++)
{
if (source[i]==search[0])
{
//找到了search中的第一个元素后,比较剩余的部分是否相等
int k=1;
while(k<search.length)
{
if (source[k+i]!=search[k])
{
continue searchForFirst;
}
k++;
}
return i;
}
}
return -1;
}
/**
**用于从一个字节数组中提取一个字节数组
**类似于 String 类的substring()
**/
private static byte[] subBytes(byte[] source,int from,int end)
{
byte[] result=new byte[end-from];
System.arraycopy(source,from,result,0,end-from);
return result;
}
/**
**用于从一个字节数组中提取一个字符串
**类似于 String 类的substring()
**/
private static String subBytesString(byte[] source,int from,int end)
{
return new String(subBytes(source,from,end));
}
/**
**返回字符串S转换为字节数组后的长度
**/
private static int bytesLen(String s)
{
return s.getBytes().length;
}
}