2011年7月31日星期日

ADF_095:如何处理BLOB类型数据之一:上传文件并保存到BLOB中

实验环境:JDeveloper 11.1.2.0.0。

1. 实验准备:创建Schema
(1)sqlplus system/welcome1 @create_neverland_user.sql
grant connect, resource to neverland identified by neverland;
Exit;
(2)sqlplus neverland/neverland @create_neveland_table.sql
DROP SEQUENCE uploaded_files_seq;
DROP TABLE uploaded_files;
CREATE TABLE uploaded_files(
id NUMBER PRIMARY KEY,
filename VARCHAR2(80),
content BLOB,
date_created DATE);
CREATE SEQUENCE uploaded_files_seq;
CREATE TRIGGER assign_file_id BEFORE INSERT ON uploaded_files FOR EACH ROW
BEGIN
SELECT uploaded_files_seq.NEXTVAL INTO :NEW.id FROM dual;
END;
.
/

2. 使用ADF BC创建Model层
(1)修改EO的ID字段的属性,因为使用Sequence,所以要改成Sequence类型。

(2)修改EO的DateCreate字段的属性,Insert时自动插入当前日期。

(3)定制化AM对应的java类,添加自定义方法:saveUploadedFileToBlob。
public void saveUploadedFileToBlob(BlobDomain content, String fileName) {
UploadedFilesViewImpl uploadedFileVO = (UploadedFilesViewImpl)getUploadedFilesView1();
UploadedFilesViewRowImpl newRow = (UploadedFilesViewRowImpl)uploadedFileVO.createRow();
uploadedFileVO.insertRow(newRow);
newRow.setContent(content);
newRow.setFilename(fileName);
getDBTransaction().commit();
}

(4)新增一个新的VO:Last5UploadedFilesView,用于显示最新上传的5个文件。
其中添加了一个自动计算字段:文件大小字段。



3. 创建一个新页面:upload_file_to_blob.jspx,核心代码如下:
(1)
<af:form id="f1" usesUpload="true">
<af:inputFile label="File Name:" id="if1" binding="#{myBackingBean.inputFileComponent}"
valueChangeListener="#{myBackingBean.inputFile_valueChangeListener}"/>
<af:commandButton text="Upload" id="cb1" actionListener="#{myBackingBean.uploadButton_actionListener}"/>
</af:form>
(2)拖放Last5UploadedFilesView,生成Table,设置Table的Partial Trigger指向Upload按钮。
(3)设置Last5UploadedFilesView1Iterator的Refresh属性=ifNeeded。
(4)增加一个action Binding:Last5UploadedFilesView1Iterator的Execute操作。

4. 对应的Manage Bean的核心代码如下:
// 点击Upload按钮时,先调用此方法,因为ValueChange事件先于actionListener事件 。
public void inputFile_valueChangeListener(ValueChangeEvent event) {
UploadedFile file = (UploadedFile)event.getNewValue();
if (file != null && file.getLength() > 0) {
String message =
"Successfully uploaded file '" + file.getFilename() + "' (" + file.getLength() + " bytes)";
popupMessage(event, message);
try {
AppModule am = (AppModule)getDefaultApplicationModule();
am.saveUploadedFileToBlob(writeInputStreamToBlobDomain(file.getInputStream()), file.getFilename());
} catch (Exception e) {
e.printStackTrace();
}
}
}

// 点击Upload按钮时,执行完valueChangeListener后,调用此方法
public void uploadButton_actionListener(ActionEvent actionEvent) {
if (this.getInputFileComponent().getValue() != null) {
// 清空InputFile,这样符合中国人的习惯。
inputFileComponent.setValue(null);
// 刷新VO:Last5UploadedFilesView,这样是为了保证从“底层”更新VO。
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Execute");
operationBinding.execute();
} else {
popupMessage(actionEvent, Not_Valid_FileName_Message);
}
}

5. 为了保证只显示最新上传的5个文件,需要修改AM的Java类,Override方法:create:
protected void create() {
super.create();
getLast5UploadedFilesView1().setMaxFetchSize(5);
}

6. 运行页面,上传文件。


Project下载:UploadFileToBlob.7z

问题1:选择某些文件时,明明文件存在,但上传时却报告:java.io.IOException: 系统找不到指定的路径。
解决方法:
其实是web.xml中配置选项:org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY的问题。
该参数的含义是上载时可以使用的最大内存,改成5M后,就可以了。原来我给的是500K,无法上传大于1M的文件,会报出该异常。
另一个参数:org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE的含义是允许上传的最大文件。我这里给的也是5M。可以根据需要修改。
为了保证上传效率,这两个参数需要配合着修改。

<context-param>
<!-- Maximum memory per request (in bytes) -->
<param-name>org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY</param-name>
<!-- Use 5,000K -->
<param-value>5120000</param-value>
</context-param>
<context-param>
<!-- Maximum disk space per request (in bytes) -->
<param-name>org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE</param-name>
<!-- Use 5,000K -->
<param-value>5120000</param-value>
</context-param>
<context-param>
<!-- directory to store temporary files -->
<param-name>org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR</param-name>
<!-- Use a TrinidadUploads subdirectory of /tmp -->
<param-value>/tmp/TrinidadUploads/</param-value>
</context-param>

参考文献:
1.http://thepeninsulasedge.com/blog/?p=6
2.http://thepeninsulasedge.com/blog/?p=50
3.http://blogs.oracle.com/smuenchadf/examples/#85
4.http://cn.forums.oracle.com/forums/thread.jspa?messageID=4013464
5.http://forums.oracle.com/forums/thread.jspa?threadID=983109
6.http://forums.oracle.com/forums/thread.jspa?threadID=934144
7.http://kr.forums.oracle.com/forums/thread.jspa?threadID=858909

2 条评论:

Peter 说...

in.read(buffer) doesn't work when you try to download the same file for the second time

千红一窟 说...

thanks for your comments.
alread fix it. pls download and try again.