当前位置: 代码网 > it编程>编程语言>Java > Java中的MultipartFile接口和File类解读

Java中的MultipartFile接口和File类解读

2025年02月12日 Java 我要评论
一、file类java.io.file是 java 标准库中用于操作文件和目录路径的类。它提供了很多方法,用于创建、删除、重命名、判断文件是否存在、获取文件信息等操作。获取文件信息boolean ex

一、file类

java.io.file是 java 标准库中用于操作文件和目录路径的类。它提供了很多方法,用于创建、删除、重命名、判断文件是否存在、获取文件信息等操作。

获取文件信息

  • boolean exists(): 判断文件或目录是否存在。
  • boolean isfile(): 判断是否是文件。
  • boolean isdirectory(): 判断是否是目录。
  • string getname(): 获取文件或目录的名称。
  • string getpath(): 获取文件或目录的路径。
  • string getabsolutepath(): 获取文件或目录的绝对路径。
  • long length(): 获取文件的大小(字节数)。

文件和目录操作

  • boolean createnewfile(): 创建新文件。如果文件已存在,则不创建,返回 false。
  • boolean mkdir(): 创建新目录。如果目录已存在,则不创建,返回 false。
  • boolean mkdirs(): 创建新目录及其父目录,如果不存在的话。
  • boolean delete(): 删除文件或目录。

文件路径操作

  • boolean renameto(file dest): 重命名文件或目录。如果成功,返回 true;否则,返回 false。
  • string[] list(): 返回目录下的文件和目录名数组。
  • file[] listfiles(): 返回目录下的文件和目录的 file 对象数组。

文件过滤

  • string[] list(filenamefilter filter): 返回目录下满足指定过滤器条件的文件和目录名数组。
  • file[] listfiles(filefilter filter): 返回目录下满足指定过滤器条件的文件和目录的 file 对象数组。

二、multipartfile接口

multipartfile是 spring 框架提供的一个接口,用于表示处理文件上传的对象。

它通常用于处理multipart/form-data类型的请求,例如处理文件上传的表单。

首先我们依旧可以通过源码的学习来进一步了解这个接口。

2.1 源码和方法功能

public interface multipartfile extends inputstreamsource {
    string getname();
 
    @nullable
    string getoriginalfilename();
 
    @nullable
    string getcontenttype();
 
    boolean isempty();
 
    long getsize();
 
    byte[] getbytes() throws ioexception;
 
    inputstream getinputstream() throws ioexception;
 
    default resource getresource() {
        return new multipartfileresource(this);
    }
 
    void transferto(file dest) throws ioexception, illegalstateexception;
 
    default void transferto(path dest) throws ioexception, illegalstateexception {
        filecopyutils.copy(this.getinputstream(), files.newoutputstream(dest));
    }
}
  • string getname():获取上传文件的表单字段名称
  • string getoriginalfilename():获取上传文件的原始文件名
  • string getcontenttype():获取上传文件的内容类型
  • boolean isempty():判断上传文件是否为空
  • long getsize():获取上传文件的大小,单位是字节
  • byte[] getbytes() throws ioexception:获取上传文件的字节数组表示
  • inputstream getinputstream() throws ioexception:获取上传文件的输入流
  • default resource getresource() :将 multipartfile 封装成了 resource 对象,从而可以使用 resource 接口提供的方法来操作上传文件的内容。
  • void transferto(file dest) throws ioexception, illegalstateexception:将上传文件保存到指定的文件;
  • default void transferto(path dest) throws ioexception, illegalstateexception:将上传文件保存在指定的路径下;

2.2 void transferto(file dest)

前面我们已经介绍了该方法是spring中提供的将上传文件保存到指定的文件中的抽象方法,溯源源码我们可以看到这个接口方法被三个实现类实现了,分别是commonsmultipartfile、mockmultipartfile 和 standardmultiparthttpservletrequest。

commonsmultipartfile中的方法体

我们可以看到commonsmultipartfile中的方法体主要是通过检测传进来的文件是否可用、是否存在,并在检测完成就执行写入的操作

public void transferto(file dest) throws ioexception, illegalstateexception {
        if (!this.isavailable()) {
            throw new illegalstateexception("file has already been moved - cannot be transferred again");
        } else if (dest.exists() && !dest.delete()) {
            throw new ioexception("destination file [" + dest.getabsolutepath() + "] already exists and could not be deleted");
        } else {
            try {
                this.fileitem.write(dest);
                logformatutils.tracedebug(logger, (traceon) -> {
                    string action = "transferred";
                    if (!this.fileitem.isinmemory()) {
                        action = this.isavailable() ? "copied" : "moved";
                    }
 
                    return "part '" + this.getname() + "',  filename '" + this.getoriginalfilename() + "'" + (traceon ? ", stored " + this.getstoragedescription() : "") + ": " + action + " to [" + dest.getabsolutepath() + "]";
                });
            } catch (fileuploadexception var3) {
                throw new illegalstateexception(var3.getmessage(), var3);
            } catch (ioexception | illegalstateexception var4) {
                throw var4;
            } catch (exception var5) {
                throw new ioexception("file transfer failed", var5);
            }
        }
    }

上面这段demo中可能对于this.isavailable()有疑问,我们知晓这里的this其实是该类的实例化对象,但是这里的this.isavailable()就是拿来判断目的文件是否可用,调用的就是类的内部方法,判断是否可用的条件就是该目标文件是否被加载进内存中

这里给出一个使用该方法的例子,需要注意的是这时候方法要求的是一个目标file对象,我们需要在调用该目标方法的时候就根据目标路径创建了目标的file对象。

// 获取上传文件的原始文件名
string originalfilename = stringutils.cleanpath(file.getoriginalfilename());
 
// 构建目标文件对象
file destfile = new file("/path/to/destination/directory", originalfilename);
 
try {
    // 将上传文件保存到目标文件
    file.transferto(destfile);
    return "file uploaded successfully!";
} catch (ioexception e) {
    e.printstacktrace();
    return "failed to upload the file.";
}

standardmultiparthttpservletrequest实现类

而另一个实现类standardmultiparthttpservletrequest和commonsmultipartfile的区别就在于使用standardmultiparthttpservletrequest直接上传文件的话可能会出现目录跳跃的问题,而commonsmultipartfile不会,这是因为其对路劲分隔符了相关的限制。

default void transferto(path dest)

该默认方法在实现类中被重写了,但主要的功能还是不变,就是将上传的文件写入到指定路径的path对象中实现文件上传的功能。

这里给出使用的示例代码:

// 构建目标文件路径
string uploaddirectory = "/path/to/destination/directory";
string originalfilename = file.getoriginalfilename();
path filepath = paths.get(uploaddirectory, originalfilename);
 
try {
// 将上传文件保存到目标文件
    file.transferto(filepath);
    return "file uploaded successfully!";
} catch (ioexception e) {
    e.printstacktrace();
    return "failed to upload the file.";
}

实际项目使用文件上传和下载

@postmapping("/file/upload")
    public result uploadfile(multipartfile file) {
        string originalfilename = file.getoriginalfilename();
        if (strutil.isblank(originalfilename)) {
            return result.error("文件上传失败");
        }
        long flag = system.currenttimemillis();
        string filepath = base_file_path + flag + "_" + originalfilename;
        try {
            fileutil.mkparentdirs(filepath);  // 创建父级目录
            file.transferto(fileutil.file(filepath));
            admin currentadmin = tokenutils.getcurrentadmin();
            string token = tokenutils.gentoken(currentadmin.getid().tostring(), currentadmin.getpassword(), 15);
            string url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token;
            if (originalfilename.endswith("png") || originalfilename.endswith("jpg") || originalfilename.endswith("pdf")) {
                url += "&play=1";
            }
            return result.success(url);
        } catch (exception e) {
            log.info("文件上传失败", e);
        }
        return result.error("文件上传失败");
    }

    @getmapping("/file/download/{flag}")
    public void download(@pathvariable string flag, @requestparam(required = false) string play, httpservletresponse response) {
        outputstream os;
        list<string> filenames = fileutil.listfilenames(base_file_path);
        string filename = filenames.stream().filter(name -> name.contains(flag)).findany().orelse(""); //  system.currenttimemillis() + originalfilename
        try {
            if (strutil.isnotempty(filename)) {
                string realname = filename.substring(filename.indexof("_") + 1);
                if ("1".equals(play)) {
                    response.addheader("content-disposition", "inline;filename=" + urlencoder.encode(realname, "utf-8"));
                } else {
                    response.addheader("content-disposition", "attachment;filename=" + urlencoder.encode(realname, "utf-8"));
                }
                byte[] bytes = fileutil.readbytes(base_file_path + filename);
                os = response.getoutputstream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (exception e) {
            log.error("文件下载失败", e);
        }
    }

文件上传接口

1.接口设置

  • @postmapping(“file/upload”):这个注解表明这个方法处理http post请求,请求路径是/file/upload。
  • public result uploadfile(multipartfile file):result是一个自定义的响应对象,包含了操作成功或操作失败的状态,以及相关数据。

2.文件处理

  • string originalfilename = file.getoriginalfilename():获取上传文件的原始文件名。
  • if (strutil.isblank(originalfilename)):检查文件名是否为空,如果为空,返回错误响应(result.error)。
  • long flag = system.currenttimemillis():获取当前时间戳(毫秒),用于创建唯一的文件名。
  • string filepath = base_file_path + flag + "_" + originalfilename:构建完整的文件保存路径。base_file_path指存储文件的根目录,文件名由时间戳和原始文件名拼接而成。

3.文件保存

  • fileutil.mkparentdirs(filepath):确保所有父目录都存在,如果不存在,则创建它们。
  • file.transferto(fileutil.file(filepath)):这行关键代码将上传的文件内容保存到服务器上的filepath路径。

4.令牌生成(可选)

  • admin currentadmin = tokenutils.getcurrentadmin():获取当前登录的管理员用户,这意味着实现了身份验证。
  • string token = tokenutils.gentoken(currentadmin.getid().tostring(), currentadmin.getpassword(), 15):根据管理员id、密码和15分钟的过期时间生成一个jwt令牌。

5.url构建

  • string url = "http://localhost:9090/api/book/file/download/" + flag + "?&token=" + token:构建一个指向下载接口(/api/book/file/download/)的url,包含时间戳(flag)和生成的令牌。
  • if(originalfilename.endswith("png")||originalfilename.endswith("jpg") || originalfilename.endswith("pdf")):如果是图片(png、jpg)或pdf文件,则在url后面添加&play=1,这表示服务器可能提供文件预览功能。

6.响应

  • return result.success(url):上传成功后,返回包含下载的result.success(url)响应
  • catch (exception e):处理过程中可能出现的异常,记录错误日志并返回一个通用的result.error响应。

文件下载接口

1.注解

  • (1)@getmapping("/file/download/{flag}"):使用getingmapping注解处理get请求,路径包含一个变量{flag},将被捕获并传递给方法。
  • (2)@pathvariable string flag:此注解将{flag}路径变量的值绑定到方法的flag参数。
  • (3)@requestparam(required = false) string play:此注解将可选查询参数”play”的值绑定到方法的play参数。required=false使此参数成为可选参数。

2.方法参数:

  • (1)string flag:此参数将包含作为url中{flag}传递的值。它用于标识要下载的文件。
  • (2)string play:此可选参数(来自查询字符串)可用于控制下载行为。如果其值为“1”,则文件可能会直接在浏览器中打开(内联)。否则,它将被视为常规下载。
  • (3)httpservletresponse response:此对象用于操作发送回客户端的http响应。

3.文件处理

  • (1)list<string> filenames = fileutil.listfilenames(base_file_path):此行假设存在一个fileutil类,其中包含一个listfilenames方法,该方法从base_file_path指定的目录中检索文件名列表。
  • (2)string filename = filenames.stream().filter(name -> name.contains(flag)).findany().orelse(""):此行使用java流查找包含提供的flag的文件名。
  • (3)string realname = filename.substring(filename.indexof("_") + 1):通过删除找到的文件名中的任何前缀(第一个”_”之前)来提取实际文件名。

响应处理

if ("1".equals(play)) {

                    response.addheader("content-disposition", "inline;filename=" + urlencoder.encode(realname, "utf-8"));

                } else {

                    response.addheader("content-disposition", "attachment;filename=" + urlencoder.encode(realname, "utf-8"));

                }

检查play参数的值。如果play为“1”,则将content-disposition标头设置为"inline;filename=" + urlencoder.encode(realname, "utf-8"),这通常告诉浏览器尝试直接打开文件。否则,他会将标头设置为 "attachment;filename=" + urlencoder.encode(realname, "utf-8"),这通常会强制浏览器下载文件。

  • urlencoder.encode(realname, "utf-8")使用utf-8编码对文件名进行编码,以正确处理文件名中的特殊字符和空格。
  • byte[] bytes = fileutil.readbytes(base_file_path + filename);使用fileutil类中的另一个假设方法将文件内容读入字节数组。
  • os = response.getoutputstream():从response对象获取输出流,允许代码将数据直接写入http响应体。
  • os.write(bytes);os.flush(); os.close();:将文件内容(字节数组)写入输出流,刷新流以确保发送所有数据,然后关闭流。
  • catch (exception e) { log.error("文件下载失败", e);}:此catch块捕获在文件下载过程中抛出的任何异常,并记录错误消息。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com