• SpringBoot: 浅谈文件上传和访问的坑 (MultiPartFile)


    本次的项目环境为 SpringBoot 2.0.4, JDK8.0. 服务器环境为CentOS7.0, Nginx的忘了版本.


    前言


    SpringBoot使用MultiPartFile接收来自表单的file文件,然后进行服务器的上传是一个项目最基本的需求,我以前的项目都是基于SpringMVC框架搭建的,所以在使用SpringBoot的时候进行MultiPartFile上传遇到了坑,这里说一下,其中主要包含两个坑点.

    • 使用transferTo()方法写入File时找不到文件路径.

    • 访问文件时Nginx的403 forbidden问题.


    使用transferTo()方法写入File时找不到文件路径


    在我们解决问题之前,我们先看一下封装的上传方法以及报错日志.

        public static final String BASE_PATH = "/test/";
    
    public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> upload(MultipartFile imageFile) {
    
        <span class="hljs-keyword">if</span> (imageFile.isEmpty()) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
        }
        <span class="hljs-built_in">String</span> filename = imageFile.getOriginalFilename();
        
        <span class="hljs-built_in">String</span> ext= <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">if</span>(filename.contains(<span class="hljs-string">"."</span>)){
            ext = filename.substring(filename.lastIndexOf(<span class="hljs-string">"."</span>));
        }<span class="hljs-keyword">else</span>{
            ext = <span class="hljs-string">""</span>;
        }
        
        <span class="hljs-built_in">String</span> uuid =  UUID.randomUUID().toString().replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">""</span>);
        <span class="hljs-built_in">String</span> nfileName = uuid + ext;
        <span class="hljs-built_in">String</span> dirPath = DateFormatUtils.format(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(), <span class="hljs-string">"yyyyMMdd"</span>);
        <span class="hljs-built_in">String</span> filepath = BASE_PATH.endsWith(<span class="hljs-string">"/"</span>) ? BASE_PATH+dirPath : BASE_PATH+<span class="hljs-string">"/"</span>+dirPath;
        File targetFile = <span class="hljs-keyword">new</span> File(filepath, nfileName);
        <span class="hljs-keyword">if</span> (!targetFile.exists()) {
            targetFile.mkdirs();
        } <span class="hljs-keyword">else</span> {
            targetFile.delete();
        }
        <span class="hljs-keyword">try</span> {
            imageFile.transferTo(targetFile);
        } <span class="hljs-keyword">catch</span> (IllegalStateException e) {
            e.printStackTrace();
        } <span class="hljs-keyword">catch</span> (IOException e) {
            e.printStackTrace();
        }
    
        <span class="hljs-built_in">String</span> accessUrl =  <span class="hljs-string">"/"</span>+nfileName;
        logger.debug(<span class="hljs-string">"上传文件成功 URL:"</span> + nfileName);
        <span class="hljs-keyword">return</span> accessUrl;
    }
    

    报错日志如下所示.

    java.io.IOException: java.io.FileNotFoundException: /test/20181025/be3676dffca94c6dac5e96a1a41dcd97.jpg (Is a directory)
        at org.apache.catalina.core.ApplicationPart.write(ApplicationPart.java:122)
        at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.transferTo(StandardMultipartHttpServletRequest.java:255)
        at com.dong.runline.common.utils.UploadUtils.upload(UploadUtils.java:56)
        at com.dong.runline.controller.TimeLineController.createTimeLineAction(TimeLineController.java:55)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
    

    这里就出现了java.io.FileNotFoundException错误,这是怎么造成的呢?,通过Debug下的断点我们发现是下面的位置发生了错误.

    这里我用的本地环境进行了测试,发现本地创建了这个样的一个路径,最后的图片被创建成一个这样的路径.

    在SpringMVC环境下并没有这样的问题,在SpringBoot却出现了这样的问题,那么到底怎么造成的呢?网上的很多博客写到,通过查询transferTo()方法源码找到了问题关键所在.

    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        this.part.write(dest.getPath());
    }
    

    我们接着进入write()方法.

        @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()成立,也就是我们没有使用绝对路径,那么file = new File(location,fileName);会在原来的基础上加上location路径.这就是原因所在,解决起来也很方便,网上总共有两种方案.

    • 使用绝对路径
    • 修改location的值

    第一种方案我们就不过多解释了,我们看一下如何修改location的值.我们只需要在启动类中注入如下Bean即可.把路径指向我们的存储路径.

        @Bean
        MultipartConfigElement multipartConfigElement() {
            MultipartConfigFactory factory = new MultipartConfigFactory();
            factory.setLocation(UploadUtils.BASE_PATH);
            return factory.createMultipartConfig();
        }
    

    然后我们再修改下UploadUtils中的创建File方法即可.

            String uuid =  UUID.randomUUID().toString().replaceAll("-", "");
            String nfileName = uuid + ext;
            File targetFile = new File(nfileName);
            if (!targetFile.exists()) {
                targetFile.mkdirs();
            } else {
                targetFile.delete();
            }
    


    访问文件时Nginx的403 forbidden问题


    所谓的Nginx的403错误其实就是访问权限错误.当前用户没有访问该资源的权限,这样理解的话,我们就有两种方案可行.一,降低文件访问的权限等级.二,升高用户的访问权限.下面我们一个一个来看一下如何进行实现.

    • 降低文件访问的权限等级

    降低文件的访问权限,我们只需要用到 chmod指令即可.这里简单解释一下chmod指令.

    在Mac的使用过程中我们也经常会修改 某个文件的权限,例如:

    chmod 777 file
    

    如果如上设置的话,那么任何一个用户都会对这个file文件拥有全部权限.

    那么为什么是三位数呢?这是因为这三位数分别代表着档案拥有者User、群组Group、其他Other三者的权限.也就是说拥有者的权限等级为7,群组的权限等级为7,其他权限等级也为7.

    那么为什么是7呢?这是因为一个linux文件总共有三种权限,分别是读r,写w,操作x.对应的值分别是4,2,1.当一个用户对某个文件拥有7的数值时,这时候为4+2+1,也就是说他拥有该文件全部的权限.

    上面说了 chmod指令的如何使用,那么接下来我们就可以对服务器的文件使用chmod 664 file指令,然后降低文件访问的权限等级.使全部用户都拥有文件的访问权限.但是问题来了,难道用户上传一次,我们就需要手动修改一次文件的权限,这显然是不正确的,那么我们该怎么办呢?这时候我们就需要提高用户的访问权限了.

    • 升高用户的访问权限

    提高用户的访问权限,这里其实是修改Nginx的启动者,我们把启动者设置为最多权限者,那么我们就可以访问到文件了.

    首先我们先看是谁启动了Nginx需要用到如下的指令.

     ps aux | grep "nginx: worker process" | awk '{print $1}'
    

    这里我已经做了修改,截图如下所示.

    本来要是不对Nginx的配置进行任何设置访问的话,那么第一个root应该为nobody,也就是Nginx的启动者.先前已经使用** ls -l file **指令查询了文件的权限情况,root 拥有读写权限,other没有任何权限.所以我们要把启动者改为root即可.

    打开Nginx配置文件所在的位置,{nginx}表示你的nginx安装路径.

    vi {nginx}/conf/nginx.conf
    

    添加启动者,如下所示.

    user root
    

    返回到sbin目录中,准备检测配置文件和重新启动Ngnix.

    cd ../sbin/
    

    检测配置文件的正确性

    ./nginx -t
    

    检测没有任何问题,重新启动

    ./nginx -s reload
    

    这时候即可正常访问到文件了.


    结语


    这篇博客算是日常的问题收集吧,整理一下.没什么可多说的,就是搞事.欢迎关注骚栋



          </div>
    

    原文地址:https://www.jianshu.com/p/d8666f2e698f

  • 相关阅读:
    访问修饰符
    SqlServer 查运行进程
    SqlServer 作业计时执行存储过程,杀超时进程
    VS快捷键操作
    sql server 命令
    vs 加载 dev控件
    TreeList显示[+] [-] 线条样式
    佳能MG2580S清零
    Dev WaitDialog 使用
    导出.txt / .pdf / .xls
  • 原文地址:https://www.cnblogs.com/jpfss/p/10984123.html
Copyright © 2020-2023  润新知