1. 恼人的Multipart form data

2023-05-16

文章目录

  • 1. 概述
  • 2. 问题
  • 3. 解决方案
    • 3.1 解决方案一
    • 3.2 解决方案二
    • 3.3 解决方案三
  • 4. 总结

1. 概述

      我目前在公司负责开放平台项目,使用spring-cloud-gateway作为开放平台网关,我们网关处理两类数据,都是通过post提交,一类普通数据接口,是通过application/json提交到网关的, 一类是文件上传接口,通过multipart/form-data的方式提交到网关,今天分享下使用multipart/form-data遇到的问题及解决方式。

2. 问题

      因为我们是开放平台,开发者接入开放平台,调用开放平台接口的时候会顺带提交开发者信息,方便开放平台后续业务处理,在文件上传接口中,开发者信息也是一并通过multipart/form-data的方式提交的,此时,我需要从multipart/form-data中获取解析出开发者信息,问题出现在获取开发者信息这里。
      通过api的方式获取MultipartData,是类似这样的代码:

return exchange.getMultipartData().flatMap(data ->{
   Map<String, Part> map = data.toSingleValueMap();
   Part msgIdPart = map.get("msgId");
   String msgId = parseFormFieldPartValue(msgIdPart);
   record.setMsgId(msgId);

   Part orgIdPart = map.get("orgId");
   String orgId = parseFormFieldPartValue(orgIdPart);
   exchange.getAttributes().put(OpenApiConstants.ORGID, orgId);

   Part appIdPart = map.get("appId");
   String appId = parseFormFieldPartValue(appIdPart);
   exchange.getAttributes().put(OpenApiConstants.APPID, appId);

      获取MultiparData非常完美,但问题是WebFlux的body数据只能打开一次,当我获取完MultipartData之后,想通过gateway把body数据传输到后台服务,此时body数据为空,后台服务获取不到数据。

3. 解决方案

3.1 解决方案一

      既然body数据只能打开一次,那打开之后能不能在设置body呢,application/json类型的接口我就是这么做的,区别是application/json数据可以把Flux中的数据转换成字符串类型,比较好处理。multipart/form-data数据需要借助api,处理成Part类型的数据,怎样把Part类型的数据在转化成body呢,我尝试了很多种方式,最终以失败告终。因为工期等原因,我没有太深入的研究这部分内容,如果各位有好的方式,还望多多指教,如果后期我研究出来了,我会来更新。

3.2 解决方案二

      第二种解决方案,是阻断gateway的filter链,此时不在通过gateway本身的机制把请求转发到后台服务了,而是直接发起对后台的调用。这种方式比较复杂,需要考虑分布式的一些问题,比如如何获取后端服务,如何对后端服务进行负载均衡调用等。工期也不允许我这么做,所以我没有采用第二种方式。

3.3 解决方案三

      第三种方案,就是把multipart/form-data数组转换成string(在这之前我借用了网上一种方式,缓存body,使body可以多次获取,但是缓存的方式与通过api获取MultipartData数据互斥),然后解析string,数据转换成string之后,是类似如下的格式:

--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="file"; filename="测试文件上传.txt"
Content-Type: application/octet-stream

测试文件上传
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="uploader"

test
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="appId"

testapp
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="isTemp"

0
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="tenantId"

000000
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="msgId"

c0dc333c-373a-4326-87ec-ddd64e2bf081
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="orgId"

orgId
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_
Content-Disposition: form-data; name="timestamp"

1661162528991
--joHDquiO2YE1x7arAlPHp9qOVpgrL-1q_--

      当上传的是文本文件的时候,这个格式还勉强可以接受,如果上传文件是类似图片的二进制数据,格式就非常难以阅读了,不过不影响解决问题,
      我写了一段儿代码,来解析这种格式,在windows和linux上亲测可用,虽然非常难看,但确实可以解决问题,代码如下所示:

public static Map<String, String> readMultiFormData(String s, String splitStr, List<String> fileParam) {
   try {
      log.info("MultiFormDataUtil.readMultiFormData lineSeparator:{} , s : {}, splitStr : {} ", System.lineSeparator(), s, splitStr);
      String [] strArray = s.split(splitStr);
      String sysSeparter = System.lineSeparator();
      Map<String, String> paramMap = new HashMap<>();

      Arrays.stream(strArray).map(item ->{
         try {
            return item.replaceAll(sysSeparter, ";");
         } catch (Exception e) {
            return "";
         }
      }).filter(item ->{
         //去除空的
         item = item.trim();
         return StringUtils.isNotEmpty(item);
      }).map(item->{
         item = item.trim();
         while (';' == item.charAt(0)) {
            item = item.substring(1);
         }
         while (';' == item.charAt(item.length()-1)) {
            item = item.substring(0, item.length()-1);
         }
         return item;
      }).filter(item ->{
         return item.contains("Content-Disposition");
      }).forEach(item ->{
         parseData(paramMap, item, ";", fileParam);
      });

      return paramMap;

   } catch (Exception e) {
      log.error("MultiFormDataUtil.readMultiFormData error " + e.getMessage(), e);
      return Collections.EMPTY_MAP;
   }
}

private static Map<String, String> parseData(Map<String, String> paramMap, String str, String sep, List<String> fileParam) {
   log.info("MultiFormDataUtil.parseData str:{}, sep:{}", str, sep);
   String key = "";
   String[] array = str.split(sep);
   for(int i=0; i<array.length; i++) {
      String ss = array[i].trim();
      if(ss.startsWith("name=")) {
         key = ss.substring(ss.indexOf('=') + 2, ss.length()-1);
         key = key.trim();
         String value = "";
         if(fileParam.contains(key)) {
            //是file时寻找file名称
            value = findFileName(array);
            value = value.trim();
         } else {
            //不是file时计算key的值
            value = array[array.length-2];
            value = value.trim();
         }

         if(paramMap.get(key) == null) {
            paramMap.put(key, value);
         } else {
            String oldValue = paramMap.get(key);
            paramMap.put(key, oldValue + ";" + value);
         }
         break;
      }
   }
   return paramMap;
}

/**
 * 解析file名称
 * @return
 */
private static String findFileName(String [] arrays) {
   try {
      String fileName = Arrays.stream(arrays).filter(item ->{
         return item.contains("filename") ? true : false;
      }).map(item ->{
         item = item.substring(item.indexOf('=') + 2, item.length()-1);
         return item;
      }).findFirst().orElse("");
      return fileName;
   } catch (Exception e) {
      return "";
   }
}

      入口方法readMultiFormData需要三个参数
      第一个参数是待解析的文本内容。
      第二个参数是分隔符,可以通过contentType.getParameter(“boundary”)的方式获取
      第三个参数是上传文件的参数名,我代码里是file
      解析出来的结果如下图所示:
在这里插入图片描述
      有用的到的朋友可以尝试下,如果大家有更好的解决这个问题的方式,不要忘了告诉我,感谢!

4. 总结

      本文介绍了在spring-cloud-gateway中通过multipart/form-data上传文件时,获取参数的三种思路并给出了第三种思路的实现方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

1. 恼人的Multipart form data 的相关文章

  • 【python】pyinstaller使用 --add-data --add-binary打包额外资源

    为什么要使用 add data 程序里文件格式有很多种 xff1a 源代码 py 图片格式 png jpg ico 等 配置文件 ini json xml等 其他可执行文件 bin exe等 模型文件 pth 等 说明文档 txt md等
  • ROS报错:TF_OLD_DATA ignoring data from the past for frame leftwheel_link

    现象 xff1a rviz 中提示no transform from XX to odom 原因 xff1a lt param name 61 34 use sim time 34 value 61 34 true 34 gt 中use s
  • idea 里form表单action提交servlet文件出现报错

    由于在action的字符串中加了空格 xff0c 导致于于url pattern不匹配 xff0c 所以报错
  • Cursor

    Mac安装使用Mysql教程 从零开始 第一章 Mac安装MySQL 1 1 过程记录 1 2 参考 第二章 安装数据库管理软件DBeaver 2 1 过程记录 2 2 参考 第三章 DBeaver创建MySQL数据库 3 1 过程记录 3
  • vue form表单最简写法

    vue form表单最简单写法 div div
  • 使用 Retrofit 1.8.0 的多部分请求不起作用

    我花了大概 4 天的时间 尝试在 android 中使用 Retrofit 1 8 0 发出多部分请求 并取得了成功 我的界面看起来像这样 Multipart POST posts add json void addComment Part
  • 在分段上传请求中实现 ProgressDialog

    我正在使用以下方法将图像从 Android 上传到服务器 public void uploadMultipart getting name for the image String name editText getText toStrin
  • JavaMail BaseEncode64 错误

    我目前正在开发一个从 gmail 帐户下载附件的应用程序 现在 每次下载压缩附件时都会出错 但是 并非全部 有些我可以毫无错误地检索它 这是异常消息 Exception in thread main com sun mail util De
  • 如何使用 httr 发布多部分/相关内容(适用于 Google Drive API)

    我使用 httr 将简单的文件上传到 Google Drive 问题是每个文档都作为 无标题 上传 我必须修补元数据才能设置标题 PATCH 请求有时会失败 根据API 我应该能够进行分段上传 允许我将标题指定为上传文件的同一 POST 请
  • F#、FParsec 和更新用户状态

    好吧 自从我的最后一个问题没有引起任何反应 我正在朝不同的方向前进 哈哈 我找不到任何超出的例子官方文档管理用户状态或访问先前解析器的结果 注意 这段代码的作用是not编译 namespace MultipartMIMEParser ope
  • 我的代码有什么问题。数据未在本机反应中使用多部分发送或上传到服务器

    我是新来的 没有反应 我已经从此表单创建了一个表单 我正在使用多部分 formData 发送图像并将其上传到服务器 但问题是当我提交的数据没有进入服务器时 并且没有出现任何错误 所以请告诉我我写了错误的代码 或者有什么问题 我在代码中犯了什
  • Angular 文件上传进度百分比[重复]

    这个问题在这里已经有答案了 在我用 Angular 4 开发的应用程序中 用户可以将多部分文件上传到服务器 文件很大 我需要向用户显示文件上传过程的当前进度及其百分比 我该怎么做 提前致谢 这适用于 Angular 9 和 10 note
  • 具有分段文件上传功能的 Spring REST API 不适用于可选参数

    我正在使用Spring框架 但不太熟悉它 我正在编写一个 REST API 特别是一个处理文件上传的 POST 方法 其中文件是可选的 我尝试简化该方法 但仍然存在问题 这是我所拥有的 RequestMapping method Reque
  • 如何解析MultipartFormDataContent

    我正在编写一个 Web API 服务 我想在其中接受一个文件 图像 和一个包含图像关键信息的序列化对象 JSON 图像部分没有问题 但是当我添加包含反序列化对象的字符串内容时 我在尝试确定哪个是哪个并采取相应行动时遇到问题 客户端代码如下所
  • Extjs:使用提交表单上传文件

    我正在尝试使用 ExtJs 和 Spring 上传文件 excel 但没有成功 所以我希望你能帮助我 在面板中 我有一个按钮 文件上传字段 我可以通过该按钮选择一个要上传的文件 xtype fileuploadfield buttonOnl
  • Servlet 3.1 - 多部分异步处理

    我正在测试 Servlet 3 1 API 来处理多部分请求 我对同步处理某些部分 文本字段 和异步处理其他部分 文件字段 感兴趣 乍一看 它似乎在 Servlet 3 1 中不可用 要么在异步上下文中通过 request getInput
  • 使用 ngx-mat-file-input 从 Angular 将文件上传为 multipart/form-data

    我在用着ngx mat file input 0 检索用户输入的文件 我想将其上传到服务器 端点需要一个多部分文件 我怎样才能做到这一点 0 https www npmjs com package ngx material file inp
  • 在 Angular 中处理多部分响应主体

    我在 Angular 中收到多部分响应正文 但应用程序未正确处理响应 事实证明 Angular 中的 HttpClient 无法正确解析多部分响应主体 请参阅这个问题在 GitHub 上 https github com angular a
  • Tomcat 8 中的多部分文件最大大小配置[重复]

    这个问题在这里已经有答案了 我的应用程序服务器是 Tomcat 我需要将文件 大尺寸 上传到我的应用程序 车身尺寸为POSTrequests 确实很长 大约 15 MB 或更多 我可以设置任何类型的配置或代码来解决这个问题吗 请记住 这是m
  • Ajax 将文件上传到内容类型为 Multipart 的 GoLang 服务器

    我正在尝试使用多部分表单将音频文件上传到 Golang 服务器 然而 Go 返回错误 multipart NextPart bufio buffer full 我相信这表明我的 Javascript 请求中存在不属于多部分格式的内容 这是我

随机推荐