您的位置:首页 > 其它

jfinal 临时文件下载(用户下载完成后删除服务器的文件)

2014-05-16 20:27 218 查看
本人在使用jfinal做一个项目的时候,需要下载一些临时文件,就是有程序产生,并且被用户下载后就失效的小文件,在使用jfinal的
renderFile(file);
后,调用 file.delete();发现,文件无法下载,于是找问题

查看jfinal controller的renderFile源码后发现,调用render系列方法后,并没有马上执行render操作,代码如下:

/**
* Render with file
*/
public void renderFile(String fileName) {
render = renderFactory.getFileRender(fileName);
}

/**
* Render with file
*/
public void renderFile(File file) {
render = renderFactory.getFileRender(file);
}

由此可见,renderFile只不过是产生了一个render对象而已,这样就能解释了,因为调用renderFile后我们把文件删除了,于是就无法下载了,于是继续找,然后找到调用这个方法的地方,发现是在一个叫做
ActionHandler
的了类里面,找到这个类的handler方法,关键部位代码如下:

Controller controller = action.getControllerClass().newInstance();
controller.init(request, response, urlPara[0]);

if (devMode) {
boolean isMultipartRequest = ActionReporter.reportCommonRequest(controller, action);
new ActionInvocation(action, controller).invoke();
if (isMultipartRequest) ActionReporter.reportMultipartRequest(controller, action);
} else {
new ActionInvocation(action, controller).invoke();
}

Render render = controller.getRender();
if (render instanceof ActionRender) {
String actionUrl = ((ActionRender)render).getActionUrl();
if (target.equals(actionUrl))
throw new RuntimeException("The forward action url is the same as before.");
else
handle(actionUrl, request, response, isHandled);
return ;
}

if (render == null)
render = renderFactory.getDefaultRender(action.getViewPath() + action.getMethodName());

render.setContext(request, response, action.getViewPath()).render();

我们发现在
new ActionInvocation(action, controller).invoke();
后才去执行的render方法,
render.setContext(request, response, action.getViewPath()).render();


问题已经找到了,于是我们需要解决,刚开始问群里的时候,大家都是建议使用定时删除,队列的方式来执行,我个人不喜欢这种绕圈子的做法,于是有了方案1

方案1

自己写代码去renderFile,这样就可以避免这个问题了,代码大致如下(基本上是从
FileRender
类中拷贝而来):

String contentType = getRequest().getServletContext().getMimeType(file.getName());
if (contentType == null) {
contentType = "application/octet-stream";       // "application/octet-stream";
}

System.out.println(contentType);

getResponse().setContentType(contentType);
getResponse().setContentLength((int) file.length());
OutputStream out = getResponse().getOutputStream();
FileInputStream in = new FileInputStream(file);

try {
byte[] buf= new byte[1024];

for(int n;(n = in.read(buf)) != -1;) {
out.write(buf,0,n);
}

} finally {
try {
in.close();
out.close();
} finally {
file.delete();
file.deleteOnExit();
}
}

这种代码解决了问题,但是还是不够好,我相信大部分人也不喜欢这种方式,虽然可以封装一个方法去做这个事情.后来用了一种更好的方式来解决这个问题,而且比较符合jfinal的设计哲学

方案2

自定义render

由于jfinal已经有了FileRender,我们可以直接继承它,我们重写 render方法就好了,这种方法做到了代码重用,个人很喜欢,代码如下:

public class TempFileRender extends FileRender {
private String fileName;
private File file;
public TempFileRender(String fileName) {
super(fileName);
this.fileName = fileName;
}

public TempFileRender(File file) {
super(file);
this.file = file;
}

@Override
public void render() {
try {
super.render();
} finally {

if(null != fileName) {
file = new File(fileName);
}

if(null != file) {
file.delete();
file.deleteOnExit();
}
}
}
}

这样在controller中使用就只需要
render(new TempFileRender(file|fileName))


打完收工
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: