MultipartFile上传文件数据库保存进了但是target里面没有图片?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MultipartFile上传文件数据库保存进了但是target里面没有图片?相关的知识,希望对你有一定的参考价值。

用ajax做的添加,然后传过去的序列化,追加了一个图像的路径,controller层添加方法里面创建了个multipartFile(这是一个集合),只修改了重写的获取图像名称的方法,然后一直到后面的transferto,都执行成功,文件夹创建出来了,数据库路劲也添加正常了,但是target里面创建出来的文件夹里面没有我上传的图片第一张图片是执行完之后MultipartFile的信息,之后的是代码

这里,我只想总结一下我一下午研究文件上传,图片上传功能的思考。也许花费了很多时间,但还是觉得比较值的。

以前一直听说过“项目使用的所有图片应该保存在图片服务器上,”一直看过这样的代码:Java后端由各种流组成的处理图片或着文件的方法。当时其实都是朦胧的,似懂非懂的样子。现在或许是有那么的一点理解了:当项目或者具体说页面中存在大量的图片时,页面的加载可能会很慢,这时,可能就需要使用一个独立的服务器来专门的读取这些图片了, 所以才有了图片服务器的说法。(或许是这样)。

图片上传功能的具体实现(当然文件也一样):

总的来说是这样的, 我们在页面上通过按钮获取到某张图片后,会显示在页面上(如果你需要的话),显示的可以是图片本身,或者是图片的名字。然后通过ajax,把图片传到了Java后台,(以什么格式我还不清楚),根据我这一下午的实践,MultipartFile ,这个类可以完全接收到前台传过来的图片数据。,这里也许有人会问, 传到后台干嘛呢。假设你的前端调用的不是你本项目的Java后台代码,而是另外一个接口Api项目的话, 那么,其实,这个APi项目就可以当作一个图片服务器了。额,这里仅仅是临时保存了图片而已,就页面而言,如果是表单,你还需要提交表单。那么表单提交时,你还会保存图片吗?应该是存图片在服务器上的地址吧。

MultipartFile 通过 MultipartFile .transferTo( new File()), 仅需要这步骤,就可以把图片存到服务器所在的电脑的任意一个盘或者路径里面。 其实这个很简单,难是难在 文件的创建。(坑了我很久),

new File("d:/test1/test2/test3/demo.png"), 类似要创建这样的 File文件时, 你要先判断demo.png 的父级路径是否存在,如果不存在则要先创建。否则你也许会报一个错误,“java.io.FileNotFoundException 拒绝访问”.

也许废话说了很多,下面直接上示例代码, 本实例代码亲测有效可用。本实例 前端采用layui + 后端 springMVC+MultipartFile

页面.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>文件上传demo</title>
<%@include file="/comm/mytags.jsp" %>
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>常规使用:普通图片
参考技术A 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据、上传影音文件等。如果文件体积比较大,或者网络条件不好时,上传的时间会比较长(要传输更多的报文,丢包重传的概率也更大),用户不能刷新页面,只能耐心等待请求完成。

下面从文件上传方式入手,整理大文件上传的思路,并给出了相关实例代码,由于php内置了比较方便的文件拆分和拼接方法,因此服务端代码使用PHP进行示例编写。

文件上传的几种方式

首先我们来看看文件上传的几种方式。

普通表单上传

使用PHP来展示常规的表单上传是一个不错的选择。首先构建文件上传的表单,并指定表单的提交内容类型为enctype="multipart/form-data",表明表单需要上传二进制数据。
参考技术B file.getPath();是指用户选择的客户端文件路径,与上传前的路径无关的。你需要取到服务器相对路径。action具体代码如下UploadForm uploadForm = (UploadForm) form; FormFile myfile = uploadForm.getMyfile();byte[] data = myfile.getFileData();ServletContext application = this.getServlet().getServletContext();String fileName = myfile.getFileName();String realPath = application.getRealPath("/temp/");//应用服务器temp的相对路径FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);//文件输出流fos.write(data);//将文件上传至temp目录下

搜索
文件上传功能本地存储
怎样彻底删除temp
电脑temp可以删除吗
java相对路径怎么写
电脑c盘的temp是什么
前端获取数据的方式

Spring Cloud Feign Client 实现MultipartFile上传文件功能

这两天老大突然交给一个任务,就是当用户关注我们的微信号时,我们应该将其微信头像下载下来,然后上传到公司内部的服务器上。如果直接保存微信头像的链接,当用户更换微信头像时,我们的产品在获取用户头像很可能会出现404异常。

由于公司运用的技术栈为spring Cloud(一些Eureka, Feign)进行服务注册和远程调用。

重点来了。。。。但直接使用FeignClient去远程调用注册中心上的上传文件接口,会一直报错。

@PostMapping
    @ApiOperation(value = "上传文件")
    public String fileUpload(@ApiParam(value = "文件", required = true) @RequestParam("file") MultipartFile multipartFile,
            @ApiParam(value = "usage(目录)", required = false) @RequestParam(value = "usage", required = false) String usage,
            @ApiParam(value = "同步(可选,默认false)") @RequestParam(value = "sync", required = false, defaultValue = "false") boolean sync) {
        if (multipartFile == null) {
            throw new IllegalArgumentException("参数异常");
        }
        String url = map.get(key).doUpload(multipartFile, usage, sync);
        return UploadResult.builder().url(url).build();
    }

远程的上传文件的接口。

@FeignClient("dx-commons-fileserver")
public interface FileServerService {


@RequestMapping(value="/file", method = RequestMethod.POST)
    public String fileUpload(
    @RequestParam("file") MultipartFile multipartFile,
    @RequestParam(value = "usage", required = false) String usage,
            @RequestParam(value = "sync", required = false, defaultValue = "false") boolean sync);
}

普通的FeignClient远程调用代码。但是这样的实现,在去调用的时候一直抛异常:MissingServletRequestPartException,"Required request part  ‘file‘ is not present"

这里去跟踪:fileServerService.fileUpload(multipartFile, null, true)源码发现发送的url是将multipartFile以url的方式拼接在query string上。所以这样的调用肯定是不行的。

 

那从百度搜索了一下关键词: feign upload 会看到有这样一种解决方案:

(原文转自:http://www.jianshu.com/p/dfecfbb4a215)

 

maven

        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form-spring</artifactId>
            <version>2.1.0</version>
        </dependency>

feign config

@Configuration
public class FeignMultipartSupportConfig {

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder() {
        return new SpringFormEncoder();
    }

    @Bean
    public feign.Logger.Level multipartLoggerLevel() {
        return feign.Logger.Level.FULL;
    }
}

feign client

@FeignClient(name = "xxx",configuration = FeignMultipartSupportConfig.class)
public interface OpenAccountFeignClient {

    @RequestMapping(method = RequestMethod.POST, value = "/xxxxx",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<?> ocrIdCard(@RequestPart(value = "file") MultipartFile file);

}

 

 

这种方案很好很强大,照搬过来就很好的解决了问题。也实现了文件上传的远程调用。

 

但是问题又来了。因为上面的成功是很大一部分源于那个配置类,里面的Encoder Bean。但我的这个项目里不止需要远程调用上传的接口,还需要调用其他的接口。这样的话会发现其他FeignClient一调用,就会抛异常。真的是一波未平一波又起。心碎的感觉。跟踪源码发现:

SpringFormEncoder的encode方法当传送的对象不是MultipartFile的时候,就会调用Encoder.Default类的encode方法。。。。。。。。。。。

public class SpringFormEncoder extends FormEncoder {
    
    private final Encoder delegate;


    public SpringFormEncoder () {
        this(new Encoder.Default());
    }


    public SpringFormEncoder(Encoder delegate) {
        this.delegate = delegate;
    }
    
    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
        if (!bodyType.equals(MultipartFile.class)) {
            delegate.encode(object, bodyType, template);
            return;
        }
        
        MultipartFile file = (MultipartFile) object;
        Map<String, Object> data = Collections.singletonMap(file.getName(), object);
        new SpringMultipartEncodedDataProcessor().process(data, template);
    }

}

而这个Encoder.Default的encode方法判断传送的类型不是String或者byte[],就会抛异常:

class Default implements Encoder {


    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) {
      if (bodyType == String.class) {
        template.body(object.toString());
      } else if (bodyType == byte[].class) {
        template.body((byte[]) object, null);
      } else if (object != null) {
        throw new EncodeException(
            format("%s is not a type supported by this encoder.", object.getClass()));
      }
    }
  }

 

就这样,我又得继续寻找其他的方法,不然没法远程调用其他的服务了。这就很尴尬。

 

那接下来就是各种FQ,各种谷歌,终于找到了合适的答案。

原文转自(https://github.com/pcan/feign-client-test   可将示例代码下载下来研究,这样方便看调用的逻辑)

 

Feign Client Test

 

A Test project that uses Feign to upload Multipart files to a REST endpoint. Since Feign library does not support Multipart requests, I wrote a custom Encoder that enables this feature, using a HttpMessageConverter chain that mimics Spring‘s RestTemplate.

Multipart Request Types

A few request types are supported at the moment:

  • Simple upload requests: One MultipartFile alongwith some path/query parameters:
interface TestUpload {
    @RequestLine("POST /upload/{folder}")
    public UploadInfo upload(@Param("folder") String folder, @Param("file") MultipartFile file);
}
  • Upload one file & object(s): One MultipartFile alongwith some path/query parameters and one or more JSON-encoded object(s):
interface TestUpload {
    @RequestLine("POST /upload/{folder}")
    public UploadInfo upload(@Param("folder") String folder, @Param("file") MultipartFile file, @Param("metadata") UploadMetadata metadata);
}
  • Upload multiple files & objects: An array of MultipartFile alongwith some path/query parameters and one or more JSON-encoded object(s):
interface TestUpload {
    @RequestLine("POST /uploadArray/{folder}")
    public List<UploadInfo> uploadArray(@Param("folder") String folder, @Param("files") MultipartFile[] files, @Param("metadata") UploadMetadata metadata);
}

 

根据上面的示例代码的提示,我也就按照上面的修改我的代码。因为原理方面没有深入的研究,所以很多代码直接复制过来修改一下。其中有一段:

Feign.Builder encoder = Feign.builder()
                .decoder(new JacksonDecoder())
                .encoder(new FeignSpringFormEncoder());

这里的encoder是示例代码自己定义的(本人的代码也用到了这个类),decoder用的是JacksonDecoder,那这块我也直接复制了。然后修改好代码为:

@Service
public class UploadService {


@Value("${commons.file.upload-url}")
private String HTTP_FILE_UPLOAD_URL;//此处配置上传文件接口的域名(http(s)://XXXXX.XXXXX.XX)

public String uploadFile(MultipartFile file, String usage, boolean sync){
FileUploadResource fileUploadResource = Feign.builder()

  .decoder(new JacksonDecoder())
                .encoder(new FeignSpringFormEncoder())
.target(FileUploadResource.class, HTTP_FILE_UPLOAD_URL);
return fileUploadResource.fileUpload(file, usage, sync);
}
}

 

public interface FileUploadResource {


@RequestLine("POST /file")
String fileUpload(@Param("file") MultipartFile file, @Param("usage") String usage, @Param("sync") boolean sync);
}

 

其中调用上传文件的代码就改为上述的代码进行运行。但是这样还是抛了异常。跟踪fileUploadResource.fileUpload(file, usage, sync)代码,一步步发现远程的调用和文件的上传都是OK的,响应也是为200.但是最后的decoder时,抛异常:

unrecognized token ‘http‘: was expecting (‘true‘, ‘false‘ or ‘null‘)

只想说 What a fucking day!!!   这里也能出错??心里很是郁闷。。。。没办法,这个方法还是很厉害的,因为不会影响其他远程服务的调用,虽然只是这里报错。那只有再次跟踪源码,发现在JacksonDecoder的decode方法:

@Override
  public Object decode(Response response, Type type) throws IOException {
    if (response.status() == 404) return Util.emptyValueOf(type);
    if (response.body() == null) return null;
    Reader reader = response.body().asReader();
    if (!reader.markSupported()) {
      reader = new BufferedReader(reader, 1);
    }
    try {
      // Read the first byte to see if we have any data
      reader.mark(1);
      if (reader.read() == -1) {
        return null; // Eagerly returning null avoids "No content to map due to end-of-input"
      }
      reader.reset();
      return mapper.readValue(reader, mapper.constructType(type));
    } catch (RuntimeJsonMappingException e) {
      if (e.getCause() != null && e.getCause() instanceof IOException) {
        throw IOException.class.cast(e.getCause());
      }
      throw e;
    }
  }

 

其中走到: return mapper.readValue(reader, mapper.constructType(type)); 然后就抛异常啦。郁闷啊。最后不知道一下子咋想的,就尝试把这个decoder删除,不设置decoder了。那终于万幸啊。。。。全部调通了。。。。。。。所以修改完的UploadService代码为:

@Service
public class UploadService {


@Value("${commons.file.upload-url}")
private String HTTP_FILE_UPLOAD_URL;//此处配置上传文件接口的域名(http(s)://XXXXX.XXXXX.XX)

public String uploadFile(MultipartFile file, String usage, boolean sync){
FileUploadResource fileUploadResource = Feign.builder()
                .encoder(new FeignSpringFormEncoder())                 //这里没有添加decoder了
.target(FileUploadResource.class, HTTP_FILE_UPLOAD_URL);
return fileUploadResource.fileUpload(file, usage, sync);
}
}

 

 

写这篇博客是因为这个问题花费了我一天多的时间,所以我一定得记下来,不然下次遇到了,可能还是会花费一些时间才能搞定。不过上面提到的示例代码还真的是牛。后面还得继续研究一下。

 

希望这些记录能对那些和我一样遇到这样问题的小伙伴有所帮助,尽快解决问题。



















































































































以上是关于MultipartFile上传文件数据库保存进了但是target里面没有图片?的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC 将上传的 MultipartFile 保存到特定文件夹

springboot多文件上传

Spring Cloud Feign Client 实现MultipartFile上传文件功能

Excel文件上传,解析,下载(一 文件上传,使用MultipartFile来实现)

MultipartFile文件上传问题

文件上传之 MultipartFile