文章目录
微信小程序 - 前端
其中主要用到的两个API,wx.chooseImage
(选择图片)和wx.uploadFile
(将本地资源上传到服务器)
地址:微信小程序开发文档
-
wx.chooseImage
从本地相册选择图片或使用相机拍照。
代码示例:
wx.chooseImage({ count: 1, // 最多可以选择的图片张数 sizeType: ['original', 'compressed'], // 所选图片的尺寸 sourceType: ['album', 'camera'], // 选择图片的来源(从相机选图/使用相机) success (res) { // tempFilePath可以作为img标签的src属性显示图片 const tempFilePaths = res.tempFilePaths } })
-
wx.chooseImage
将本地资源上传到服务器。客户端发起一个HTTPS POST请求,其中
content-type
为multipart/form-data
。代码示例:
wx.chooseImage({ success (res) { const tempFilePaths = res.tempFilePaths wx.uploadFile({ url: 'https://example.weixin.qq.com/upload', //开发者服务器地址 filePath: tempFilePaths[0], // 要上传的资源的路径 name: 'file', // 文件对应的 key,开发者在服务端可以通过这个 key 获取文件的二进制内容 formData: { 'user': 'test' }, // HTTP 请求中其他额外的 form data success (res){ const data = res.data // 开发者服务器返回的数据 } }) } })```
后端代码
项目采用SpringCloud,会经过一个前置模块进行远程调用,用到Feign(这个需要特别注意!)
-
前置模块(调用模块)
@PostMapping("file/uploadFile") @ApiOperation(value = "上传文件", notes = "上传文件") public ApiResponse uploadFiles(HttpServletRequest request, @RequestParam(value = "file", required = false) MultipartFile file){ return feignBusinessService.uploadFiles(request,file); }
-
FeignService
openfeign默认不支持文件参数,但提供了feign-form扩展工具。
引入io.github.openfeign.form:feign-form:3.8.0
和io.github.openfeign.form:feign-form-spring:3.8.0
maven依赖,注入 SpringFormEncoder ,在 @FeignClient 中配下configuration即可。
Maven依赖
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>10.7.4</version> <scope>compile</scope> </dependency>
配置类
public class FeignMultipartSupportConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } }
接口编写
- @FeignClient使用配置类
- @PostMapping设置 consumes = MediaType.MULTIPART_FORM_DATA_VALUE
- 使用@RequestPart(),不能使用@RequestParam()
@Component @FeignClient(name = "businessApi",configuration= FeignMultipartSupportConfig.class) public interface FeignBusinessService { /** * 上传文件 * 也可以使用MultipartFile[]上传多个文件 * @param request * @param file * @return */ @PostMapping(value = "app/file/uploadFile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE) ApiResponse uploadFiles(@RequestParam("request") HttpServletRequest request, @RequestPart("file") MultipartFile file); }
-
业务模块(被调用模块)
/** * 上传文件 * * @param request * @param file * @return * @throws IOException */ @PostMapping("file/uploadFile") @ApiOperation(value = "上传文件", notes = "上传文件") public ApiResponse uploadFiles(HttpServletRequest request, @RequestParam(value = "file", required = false) MultipartFile file) throws IOException { request.setCharacterEncoding("UTF-8"); log.info("执行图片上传"); if (!file.isEmpty()) { log.info("成功获取照片"); String fileName = file.getOriginalFilename(); String path; String type; type = fileName.indexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1) : null; log.info("图片初始名称为:" + fileName + " 类型为:" + type); if (type != null) { if ("GIF".equals(type.toUpperCase()) || "PNG".equals(type.toUpperCase()) || "JPG".equals(type.toUpperCase())) { // 上传到本地 // // 项目在容器中实际发布运行的根路径 // String realPath = request.getSession().getServletContext().getRealPath("/"); // // 自定义的文件名称 // String trueFileName = System.currentTimeMillis() + fileName; // // 设置存放图片文件的路径 // path = realPath + "/uploads/" + trueFileName; // log.info("存放图片文件的路径:" + path); // File path1 = new File(path); // if (!path1.exists()) { // path1.mkdirs(); // } // file.transferTo(path1); // log.info("文件成功上传到指定目录下"); // 上传到服务器 String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")); String trueFileName = StringUtils.getSimpleNum() + suffix; boolean flag = ftpUtil.uploadByBytes(remoteWay, file.getBytes(), trueFileName); if (!flag) { return ApiResponse.getApiResponse("添加失败"); } return ApiResponse.getApiResponse(ftpUtil.getLookPath(remoteWay + "/" + trueFileName)); // 返回服务器返回的图片地址 } else { log.info("不是我们想要的文件类型,请按要求重新上传"); return ApiResponse.getFailApiResponse(); } } else { log.info("文件类型为空"); return ApiResponse.getFailApiResponse(); } } else { log.info("没有找到相对应的文件"); return ApiResponse.getFailApiResponse(); } }
FTP工具类
@Slf4j @Component public class FileFtpUtil { /** * ftp上传单个文件 * * @param path 上传至ftp的路径名不包括文件名 * @param localFilePath 要上传的本地文件全路径名 * @throws IOException */ public boolean upload(String path, String localFilePath) { boolean result = false; //初始化ftp链接 this.initFtpClient(); //构建文件输入流 FileInputStream fis = null; try { //处理上传目录不存在。上传前先构建一次目录,避免文件目录不存在报错 ftp.makeDirectory(remote + path); // 切换上传目录 if (this.changeWorkingDirectory(path)) { File srcFile = new File(localFilePath); if (srcFile.exists() && srcFile.isFile()) { fis = new FileInputStream(srcFile); result = ftp.storeFile(srcFile.getName(), fis); if (result) { log.info("FTP=============文件上传 目录:{} 文件名:{} 成功", path, srcFile.getName()); } else { log.info("FTP=============文件上传 目录:{} 文件名:{} 失败", path, srcFile.getName()); } } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (null != fis) { fis.close(); } //关闭ftp this.close(); } catch (IOException e) { throw new RuntimeException("关闭FTP连接发生异常!", e); } } return result; } }
配置文件
ftp: ip: xxx // 服务器IP port: xxx user: xxx pwd: xxx # 文件服务器路径前缀 remote: xxx # 文件服务器路径 route-way: xxx # 文件服务器文件查看地址 look-path: xxx
遇到的BUG
-
[Current request is not a multipart request] with root cause
解决方案:检查Content-Type,设置为multipart/form-data
。也有可能不是前端的问题,后台没有接收到参数。 -
nested exception is java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode
-
添加对文件的配置后报错:
feign.codec.EncodeException: class java.util.LinkedHashMap is not a type supported by this encoder.
当时配置类如下:
@Configuration public class FeignMultipartConfig { @Bean @Primary @Scope("prototype") public SpringFormEncoder multipartFormEncoder() { return new SpringFormEncoder(); } @Bean public feign.Logger.Level multipartLoggerLevel() { return feign.Logger.Level.FULL; } }
解决方案:将配置类改为
public class FeignMultipartSupportConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } }
参考: