本来一次很简单的文件上传,在windows已经测试很多遍了,放到linux服务器偏偏不行了,然后就研究了一下,开始的时候是没有文件生成,但是也不报错,路径也看了好几遍,也没有错。
不过呢,代码虐我千百遍,我对代码如初恋。
所以,接着找错。想了一下,就换了一种方式,采用bytes,以流的方式写入。之前是以 transferTo 方式写入的。
这种方式同样的没有报错,但是也没有文件产生。我想到了权限问题,可能是权限引起。
所以加上了:
Runtime.getRuntime().exec("chmod 777 -R " + UPLOAD_USERAVATAR);
其中 UPLOAD_USERAVATAR 是文件路径,这里是绝对路径。
这步是开权限,但是最终也没能生效。
没办法,接着找错呗!
对于这行,起初是放在 transferTo 之前的 。代码结构如下:
File dest = new File(UPLOAD_USERAVATAR + file.getOriginalFilename());
Runtime.getRuntime().exec("chmod 777 -R " + UPLOAD_USERAVATAR);
log.info("---------------------------------开始头像更新!---------------------------------");
log.info("---------------------------------头像存放路径:" + UPLOAD_USERAVATAR + file.getOriginalFilename() + "----------------------");
try {
log.info("------------------------------isAbsolute:" + dest.isAbsolute() + "------------------------------------------");
file.transferTo(dest);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
log是为了测试用,忽略!之后移了一下位置:
File dest = new File(UPLOAD_USERAVATAR + file.getOriginalFilename());
log.info("---------------------------------开始头像更新!---------------------------------");
log.info("---------------------------------头像存放路径:" + UPLOAD_USERAVATAR + file.getOriginalFilename() + "----------------------");
try {
log.info("------------------------------isAbsolute:" + dest.isAbsolute() + "------------------------------------------");
file.transferTo(dest);
Runtime.getRuntime().exec("chmod 777 -R " + UPLOAD_USERAVATAR);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
但又爆出一个新的问题:
java.io.IOException: java.io.FileNotFoundException
文件或文件夹找不到。代码就是如此神奇,刚刚还是没错,这又报错。
可以肯定是 transferTo 方法报错。所以跟踪下源码。
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
//中间代码省略
/**
* Spring MultipartFile adapter, wrapping a Servlet 3.0 Part object.
*/
@SuppressWarnings("serial")
private static class StandardMultipartFile implements MultipartFile, Serializable {
//中间代码省略
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
this.part.write(dest.getPath());
}
}
}
可以看到,调用的是write。我们接着进入write:
package org.apache.catalina.core;
/**
* Adaptor to allow {@link FileItem} objects generated by the package renamed
* commons-upload to be used by the Servlet 3.0 upload API that expects
* {@link Part}s.
*/
public class ApplicationPart implements Part {
//中间代码省略
@Override
public void write(String fileName) throws IOException {
File file = new File(fileName);
if (!file.isAbsolute()) {
file = new File(location, fileName);
}
try {
fileItem.write(file);
} catch (Exception e) {
throw new IOException(e);
}
}
}
重点来了, file.isAbsolute() 这是判断是否使用相对路径。如果没有使用绝对路径,那么就会在前面加上一个location路径。
但是我的 UPLOAD_USERAVATAR 明明是绝对路径:
/opt/images
这看着好像没错啊,但是就是出不来。具体原因呢,且听我慢慢道来。
不过眼下这个问题还是得解决。
启动类加上如下代码:
/**
* 文件上传临时路径
*/
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation("/opt/images");
return factory.createMultipartConfig();
}
我们就直接手动设置喽。设置完倒是不报错了,但是一朝回到解放前,不报错,文件也没有。
现在权限试了,路径问题试了,流也试了,能试的也都试了。可能是冥冥之中的天意吧,哈哈哈哈哈。
突然想到,会不会已经生成了,只是不在预期文件夹内。
于是,根目录下找呗。
find -name fileName.file
还真别说,真给揪出来了。在哪呢,docker容器里。得,问题一下子找到了。
这里涉及一个挂载也就是docker容器和宿主机目录映射的问题。
涉及到一个概念:
卷
docker的持久化存储卷。是容器上的一个或多个“目录”,此类目录可绕过联合文件系统,与宿主机上的某个目录“绑定(关联)”;
而使用卷呢,做个假设,在底层运行一个存储库mysql,mysql本来对于I/O的要求就比较高,如果mysql又是运行在容器中自己的文件系统之上时,也就是容器在停止时,就意味着删除,其实现数据存取时效率比较低,要避免这个限制要使用存储卷来实现。
但是卷也很不好用,和宿主机直接的联系很麻烦。容器间共享数据很麻烦,删除容器后,数据也就连带着没了。
好了,其余的自己了解去吧。接着刚刚的问题。问题根源找到了,那就解决呗。
既然是卷,那就创建映射,但是如果仅仅是docker的话,直接执行命令就好了。
docker run --name house -d -p 8099:8099 -p 8098:8098 -v /opt/nginx/html:/opt/nginx/html -t house:v7
只是我这使用的是 docker-compose 。就很烦,但是问题总得解决不是。
经过草船借箭,外加自己尝试,出来了:
volumes:
- ./webroot/avatar:/webroot/avatar
这里也涉及到一个参数:volumes
也就是卷,具体的自己找。这里我介绍个大概,./webroot/avatar 这个呢,就是你自己的容器往上一级。
注意:不是根目录啊。
我的总容器位置:
/opt/airsystem
需要启动的容器,也就是jar所在位置:
/opt/airsystem/webapp
所以呢,./就是上级目录了。 ./webroot/avatar 这个路径从根目录看就是:
/opt/airsystem/webroot/avatar
而 UPLOAD_USERAVATAR 这个的路径则是: /webroot/avatar
好了,问题解决了。
所以呢,其实本来这个问题应该在第一次就想到docker的,但是偏偏想到了权限,也算是南辕北辙了。
不过好在解决了,所以啊,遇到问题,慢慢解决,没有无缘无故的错,更没有无缘无故的bug,总能解决的。
欢迎各位同道一起讨论。