记录一次docker-compose 上传文件所遇到的坑

本来一次很简单的文件上传,在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,总能解决的。

欢迎各位同道一起讨论。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哇呜哦~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值