MultipartFile.transferTo(targetFile)

MultipartFile.transferTo(targetFile),第1张

时间:2020.12.4

异常:java.io.FileNotFoundException: /home/jdp/apache-tomcat-8.5.54-8-group/apache-tomcat-8.5.54-80 /work/Catalina/localhost/ROOT/upload_cda0de04_fa66_4589_8170_5cb0cb7f8152_00000005.tmp

初步判断:文件上传路径问题,但是项目路径中并未配置这个路径,后面了解到 /work 是tomcat 编译文件的临时路径,联想到 /work 之后的文件可能是上传的临时文件路局侍径,而前面的路径是tomcat的绝对路径

探索1:项目配置的文件路径为/home/temp, 可能是因为tomcat的临时文件路径和我们上传的临时文件路径不匹配导致的

尝试1:设置tomcat 临时文件路径 在 application.properites 中配置 增加配置 server.tomcat.basedir=/home/temp

结果1:未解决,报错不变

探索2:查阅资料得知 MultipartFile.transferTo(targetFile) 方法是面向绝对路径的,如果入参是一个相对路径,则会在路径之前加上 一个系统绝对路径(比较符合我们的情况)

尝试2:根据文章所述:增咐腊余加配置类 指定临时文件的路径

@Configuration

public class FileUploadConfig {

    @Value("${file.temp.root}")

    private String FILE_TEMP_ROOT

    /**

    * 文件上传配置

    *

    * @return MultipartConfigElement

    */

    @Bean

    public MultipartConfigElement multipartConfigElement(

            @Value("${multipart.maxFileSize}") String maxFileSize,

            @Value("${multipart.maxRequestSize}") String maxRequestSize) {

        MultipartConfigFactory factory = new MultipartConfigFactory()

        // 单个文件最大

        factory.setMaxFileSize(maxFileSize)

        // 设置总上传数据总大小

        factory.setMaxRequestSize(maxRequestSize)

        //上传临时路径

        File file = new File(FILE_TEMP_ROOT)

        if (!file.exists()) {

            file.mkdirs()

        }

        factory.setLocation(FILE_TEMP_ROOT)

        return factory.createMultipartConfig()

    }

}

结果2:报错发生变化,变成了我们的指定的路径加上 临时文件名,但是依旧未能解决问题

java.io.FileNotFoundException: /home/temp/upload_a8eb4246_470f_4f73_81af_891f1ad6651b_00000002.tmp

转换思路:既然是transferTo()方法的问题,换一个方法

探索3:更换transferTo()方法衡滚,使用 FileUtils.copyInputStreamToFile(sourceFile.getInputStream(), targetFile)方法

尝试3:寻找方法中包涵的transfTo()方法,替换成FileUtils.copyInputStreamToFile(sourceFile.getInputStream(), targetFile)

结果3:问题解决

总结:transferTo()在windows 系统上并没有这个问题,在Linux上出现了这个问题,网上的资料各有说法,目前未找到真正的原因。为了避免这个问题,统一使用FileUtils.copyInputStreamToFile(sourceFile.getInputStream(), targetFile)方法来复制文件。

最近有一个场景,在提交春乱表单的时候,需要实现添加附件的功能,

表单内容要先提交到服务端,创建一个 issue,然后再将附件添加到这个 issue 中。

所以,附件在用户添加的时候,是 没有立即上传 的,

用户可以随意在浏览器端添加和删除,issue 创建后再一起上传。

前端采用的组件库是 antd ,用到了 upload 组件。

服务端接口是自定义实现的,也许并不支持 antd upload 上传组件的规范。

服务端接受数据时,使用了 MultipartFile ,这是 Spring 框架中常用的 写法 。

我们先看看 html input[type=file] 组件默认行为,

点击 “选择文件”,浏览器会d出一个窗口,

选中一个文件,点 “打开”,就会触发 onchange 事件,

在 onchange 事件蔽森磨中,可以通过 e.target.files[0] 拿到刚才上传的那个 File 对象

再来看一下 upload 组件的默认行为,

点击 “添加”,浏览器也会d出那个选择文件的窗口,

选中一个文件,点 “打开”,发现上传失败了。

打开控制台,看到 upload 组件向 / 这个地址发送了一个 POST 请求,

数据格式如下,

我们可以向 upload 组件传入 action 参数,修改 POST 请求地址,

但是,选中文件后立即上传 不符合 我们的场景,我们需要提交表单之后,将多个文件统一上传。

所以我们得自定义 upload 组件的宏斗行为。

upload 组件的有一个 customRequest 属性( #api ),

它可以配置自定义的上传行为。

我们的思路是,先将选中后自动上传的行为取消掉,然后再在提交表单后统一上传。

取消自动上传 的实现片段如下,

我们只需要在 customRequest 回调中,调用它的 onSuccess 参数即可。

删除也是可以用的,

现在我们添加两个附件,

接着来看前端怎样将这些附件,统一上传给服务端,具体实现如下,

可以看到请求成功了(项目中的 url 跟本例稍有不同,下图只为了示意),

还有几个需要注意的点:

上文 httpClient.post 实际调用了 XMLHttpRequest 发送请求,可能会遇到 跨域 的问题。

所以在调试上传接口的时候,需要检查一下服务端的配置,是否支持跨域请求。

CORS 相关的内容大致如下:

在预检请求阶段,服务端对 OPTIONS 请求的响应头中会包含 Access-Control-Allow-Origin ,

表明服务端接受该域 http://foo.example 的跨域请求。

注:

这里需要后端实现 OPTIONS 方法,后端框架一般会通过配置方式统一处理(返回 200 或 204,不能是 4xx)。

如果未配置统一处理方式,框架可能会直接返回 404 导致预检请求失败,CORS 请求也会失败。

使用 XMLHttpRequest 发送请求时,也可以携带 cookie 信息,

同时 预检请求中服务端响应头,也要包含 Access-Control-Allow-Credentials ,否则就不会发送 cookie

对于附带 cookie 的请求,服务器不能设置 Access-Control-Allow-Origin 的值为 “ * ”,否则请求将会失败。

而将 Access-Control-Allow-Origin 的值设置为具体的地址 http://foo.example ,请求才能成功。

我们上传功能用到了携带 cookie 的跨域请求,

可以看到服务端响应头中确实包含了, Access-Control-Allow-Credentials 和 Access-Control-Allow-Origin 两个字段。

Spring: Uploading Files

Spring: org.springframework.web.multipart #MultipartFile

ant-design v4.11.1

Ant Design - Upload #API

MDN: CORS

在PHP中,可以使用$_FILES超全局变量来处理上传的文件,该变量是一个关联数组,其中包含有关上传文件的信息,例如文件名、文件类型、文件大小等。可以使用move_uploaded_file()函数将上传的文指散件保存到服务搏行器的指定目录中。

如果要将上传的二进制流文件转换为MultipartFile,需要使用第三方库来实现。例如,可以使用GuzzleHttp库将二进制数据流转换为StreamInterface对象,然后将其传递给UploadedFile类的构造函数。

下面是一个示例代码,演示如何将上传的二进制流文件转换为MultipartFile:

use GuzzleHttp\Psr7\Stream

use Symfony\Component\HttpFoundation\File\UploadedFile

// 获取上传的文件流

$fileContent = file_get_contents($_FILES['file']['tmp_name'])

// 将二进制流转换基逗哗为 StreamInterface 对象

$stream = new Stream(fopen('php://memory','r+'))

$stream->write($fileContent)

// 创建 UploadedFile 对象

$file = new UploadedFile(

$stream,

$_FILES['file']['name'],

$_FILES['file']['type'],

$_FILES['file']['size'],

UPLOAD_ERR_OK,

true // 是否删除临时文件

)

// 现在 $file 是一个 MultipartFile 对象,可以将其传递给需要的函数进行处理

注意,上述示例中使用了GuzzleHttp和Symfony两个第三方库,需要事先安装和引入这些库才能正常运行代码。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/12208745.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-21
下一篇 2023-05-21

发表评论

登录后才能评论

评论列表(0条)

保存