在WEB开发中,上传文件的操作时必不可少的一项功能。那么,在Jfinal中,关于文件上传的操作,他到底都做了些什么呢?又有什么需要注意的了?今天我们就来看看关于文件上传的那些个故事。
关于上传文件,他和普通的表单提交不一样,有啥不一样,为何不一样,怎么就不一样了?我想这个应该不用我多少吧,做WEB应用的同学们都应该知道,在有附件提交的那种表单里面,一定要加一个属性“enctype=“multipart/form-data””加了这个以后,我们就可以通过POST请求的方式将所有相关信息提交到我们的后台去了!!但是,在使用JFinal的时候,我们一定要注意一点,就是在我们后台接收这个POST数据的时候,我们一定要注意一点的就是,假如我们的POST请求中有上传附件的表单元素,也就是的时候,我们一定要将
UploadFile uf = getFile("filename","code/");
这个东西放在方法的第一行,因为这样的话,才能够接收表单元素中的非上传附件的元素的值,至于他为什么要这么去做,我们再后面会详细的介绍,先告诉大家怎么去使用,然后在去理解原理,我认为这个是在软件开发领域中普遍的学习方法吧,因为我写的这些基本上应该是属于内功,和江湖上所说的那些个“《九阴真经》”之类的书籍属于一个性质,嘿嘿,开始有点飘了!!
废话不说,进入正题:
1、如果要使用Jfinal的文件上传的话,他一定是有依赖包的,记住,一定是有依赖包,否则,你的上传操作是会出现未知的错误的,那么这个依赖包是啥?从哪儿下。
答案:依赖包是“cos.jar”至少我的是这个,如果版本有更新的话,你都可以从Jfinal的那个官网上面去下载下来,这样的,你就可以下载到最新的那个jar包了,不过应该不会经常变吧,否则就用户会受不了的
好了第一个问题解决,在我们假如jar包以后,我们就可以来进行文件上传了。
按照我刚刚说的那个,在你要处理的方法的第一行 写上上述的那一句接收的方法,那么你的文件上传基本算作完了,是的,就是这样,此时的文件已经到了你的默认上传文件的目录项目下面了,只是此时,他的名称和你本地的名称一样,不过这只是第一步,也就是所,他的文件是不会放到一个临时的文件夹或者之类的地方,而是直接给弄过去到一个他默认的位置的。文件夹的名称叫做“upload”,现在我们就看看这个过程
由于我们再Controller中使用getFile()这个方法来接收相关的文件,所以我们就从这个地方下手,看看Jfinal框架的本身对这个过程都做了什么样的操作。
打开Controller这个类,找到getFile这个方法,我们看看都有些啥:
public UploadFile getFile(String parameterName, String saveDirectory) {
getFiles(saveDirectory);
return getFile(parameterName);
}
public UploadFile getFile(String parameterName, String saveDirectory, Integer maxPostSize, String encoding) {
getFiles(saveDirectory, maxPostSize, encoding);
return getFile(parameterName);
}
public UploadFile getFile(String parameterName) {
List<UploadFile> uploadFiles = getFiles();
for (UploadFile uploadFile : uploadFiles) {
if (uploadFile.getParameterName().equals(parameterName)) {
return uploadFile;
}
}
return null;
}
以上是列举了一些getFile的方法,我们看看他们有什么不同,
第一个GetFile 他接收的参数是parameter和SaveDirectory
Parameter是表单里面的file对应的name属性值,大家应该都明白吧,就是去接收谁的参数
第二个参数就是savedirectory,这个主要是表示当前接收的文件存放在什么位置;举个例子;假如我们的文件上传的根路径是upload(Jfinal默认的),那么我们假如使用这个方法的话,填写了saveDirectory,假如是“a”,那么文件就会被放在“upload/a/”这个路径下面,请注意的是,假如我们的savedirectory的文件夹不存在的话。Jfinal会自动创建出这个文件夹。
然后再往下看,第二行调用了getfiles()参数神马的我就不说了,一看就能够明白是什么意思。无非就是写编码,最大允许上传之类的参数一看就应该明白
然后调用getFile(Param)去处理文件,其实如果你细心的话你可以看到,他的处理过程是把一个上传文件看做是多个上传文件的一个特例,通过循环迭代出来处理各个上传文件的。至此 我们第一个问题算是解决了,就是在getFile的过程中,他的底层是怎么去处理的,如果你看到这里,如果还是一知半解的话,我强烈的建议你去看看这个JFinal框架的源代码。写得还是比较的容易懂的。
好了,我们来解决下一个问题,那就是,为什么在有上传文件的过程当中,他一定要将getFile这个方法写在最前面,否则不管怎么样都会不能能接受的非上传文件的参数,我相信,初次用JFinal框架做WEB项目的时候肯定,一定以及绝对的遇到过这个问题。不过这个问题的说明在文档中有提到过的,不过我第一次使用这个框架的时候,确实没有好好看过这方面的东西,所以遇到这个问题的时候,我足足搞了一天,在网上各种搜索,没有任何结果,心中有一万匹草泥马在心中奔腾,检查各种配置都没有问题,尼玛拿到是个灵异事件。后来看文档,才焕然大悟。原来一定要这么干才能够拿到想要的东西,框架么,肯定有自己的一套规矩。所以走走弯路,有时候收获的东西会更加多。
所以,以后再遇到这种尼玛各项配置都OK的,又有文件上传的情况,你先看看getFIle这个东西是不是在你要处理方法的第一行,如果没有的话,问题可能就出在这里,你要问为什么,也许一会儿会有答案,不过你可以先记住一点就是,框架要求这样做的,也许这能够给你一点点心里安慰,但假如你是把这个作为完美解决方案的话,那么接下来相当有营养的东西,你可能就要错过了。
首先说说,为什么JFinal在没有配置上传文件根目录的情况下,他会在项目中创建一个叫upload的文件夹
private void initOreillyCos() {
Constants ct = constants;
if (OreillyCos.isMultipartSupported()) {
String uploadedFileSaveDirectory = ct.getUploadedFileSaveDirectory();
if (uploadedFileSaveDirectory == null || "".equals(uploadedFileSaveDirectory.trim())) {
uploadedFileSaveDirectory = PathKit.getWebRootPath() + File.separator + "upload" + File.separator;
ct.setUploadedFileSaveDirectory(uploadedFileSaveDirectory);
/*File file = new File(uploadedFileSaveDirectory);
if (!file.exists())
file.mkdirs();*/
}
OreillyCos.init(uploadedFileSaveDirectory, ct.getMaxPostSize(), ct.getEncoding());
}
}
在Jfinal类中,你找到一个叫initOreillyCos()的方法,别说你不会找方法的快捷键啊“ctrl+o”,不解释,只要你使用的是默认的配置。这个肯定管用的。
其中有一句话就是说明我们刚刚的这个问题的:
uploadedFileSaveDirectory = PathKit.getWebRootPath() + File.separator + "upload" + File.separator;
喏,啥也不说了,在Jfinal初始化的时候,请注意他的判断条件,他就会建立一个默认的文件夹,格式就是“项目根路径”+upload+”/”。记住这个过程是在你没有配置上传文件根路径的时候,系统默认的,不过这个路径会有个问题,有啥问题,我们一会儿讨论,不过如果你要使用上传的话 我个人是不建议使用系统默认的这个,因为很悲剧。没错,确实是很悲剧的。
好了,我们讨论了默认上传路径以后,我们需要继续前进,然后解决其他相关的问题。
第二个问题,在上传文件的时候,为什么要把GetFile放在处理方法的第一行。
在我们刚刚提到的处理上传文件的过程中,假如了enctype=“multipart/form-data, 以后 此时的request对象就不是我们使用普通表单提交的request对象了,我们假设加了刚刚的那个属性的方式的request对象叫做MutipartRequest对象,那么我们以getMOdel()这样的方法的举例
public <T> T getModel(Class<T> modelClass) {
return (T)ModelInjector.inject(modelClass, request, false);
}
在调用这个方法的时候,我们看到 他执行了inject这个方法,正好这个方法里面有request对象,按照我们普通表单提交以后产生的这个request对象,我们是能够通过反射建立这个model实例,然后遍历其中的属性为我们的model实例进行相应的赋值操作,不过我们假设我们提交了这个带有附件上传的表单,那么我们产生的request对象就不是我们平常见到那个request对象,而是通过如下代码产生的request对象:
public List<UploadFile> getFiles(String saveDirectory, Integer maxPostSize, String encoding) {
if (multipartRequest == null) {
multipartRequest = new MultipartRequest(request, saveDirectory, maxPostSize, encoding);
request = multipartRequest;
}
return multipartRequest.getFiles();
}
通过如上的代码,我们可以看到,request对象已经变成mutipartRequest对象了,这个对象里面,我猜,他包含了原来普通request对象内容和我们上传附件里面的一些个内容,这样的话,再将这个mutipartRequest对象传入getModel里面,这样通过遍历这里面的相应内容,从而就能够得到我们想要的文件相应的数据和我们非上传文件中的内容了,这也就能够解释,为什么我们再不使用getfile这个方法在最开始的时候,得到model中的所有属性值都为null,因为我们model实例是被反射创建的,而这赋值的过程但中,他并没有拿到相应的值,所有全部为NULL;
简单总结一下:
就是在我们进行非附件表单提交的时候,得到的request对象时我们JavaServlet的中的request对象,而在我们进行带附件上传的时候得到的对象其实是包含了这个标准的Request对象内容的MutipartRequest对象,至少
multipartRequest = new MultipartRequest(request, saveDirectory, maxPostSize, encoding);
request = multipartRequest;
这个证明了我刚刚说的观点,此时的request对象非彼时的request对象。
欢迎拍砖指正啊!!