【问题标题】:Parsing multipart HTTP form data with file upload content using Scala使用 Scala 解析带有文件上传内容的多部分 HTTP 表单数据
【发布时间】:2012-03-19 10:19:23
【问题描述】:

那里有很多multipart/form-data 文件上传解决方案,但我无法为 Scala 找到一个独立的解决方案。

Play2 将此功能作为框架的一部分,Spray 还支持多部分表单数据。不幸的是,这两个似乎都很好地集成到了其余的工具集中(我可能在这里错了)。

我的服务器是使用 Finagle 开发的(目前不支持多部分表单数据),如果可能的话,我想使用一个独立的库或“滚动我自己的”解决方案。

这是一个典型的多部分/表单数据消息:

--*****org.apache.cordova.formBoundary
Content-Disposition: form-data; name="value1"

First parameter content
--*****org.apache.cordova.formBoundary
Content-Disposition: form-data; name="value2"

Second parameter content
--*****org.apache.cordova.formBoundary
Content-Disposition: form-data; name="file"; filename="image.jpg"
Content-Type: image/jpeg

$%^&#$%^%#$
--*****org.apache.cordova.formBoundary--

在本例中,*****org.apache.cordova.formBoundary 是表单边界,因此分段上传包含 2 个文本参数和一个图像(为了清楚起见,我将图像数据连接起来)。

如果比我更了解 Scala 的人能给我一些关于如何解析此内容的简要说明,我将不胜感激。

首先,我想我会很快将内容分成三部分:

data.split("\\Q--*****org.apache.cordova.formBoundary\\E") foreach println

但执行速度非常慢(更新 - 这是由于预热时间)。有没有更有效的方法来拆分零件?我的策略是将内容拆分为部分,然后将部分拆分为子部分。这是一个糟糕的方法吗?我已经看到状态机解决了类似的问题?什么是好的功能方法。请记住,在尝试解决问题的同时,我正在尝试学习正确的 Scala 方法。

更新:

我真的认为解决这个问题的方法是在 Scala 中写一两行代码。如果有人用巧妙的解决方案偶然发现了这个问题,请花时间记下来。据我了解,可以使用模式匹配、解析组合器、提取或简单地拆分字符串来解析此消息。我正在尝试找到解决此类问题的最佳方法,因为我正在从事的项目涉及大量自然语言解析,并且我需要编写自己的自定义解析工具。我对 Scala 有了很好的了解,但没有什么比专家的建议更好的了。

这不仅仅是解决问题,而是要找到解决此类问题的最佳(希望是最简单的)方法。

【问题讨论】:

  • 你可以在这里找到播放代码github.com/playframework/Play20/blob/master/framework/src/play/… 似乎可以理解
  • 感谢@Paul。我看了一下播放代码,谢谢你的链接。我确实了解其中的大部分内容,但是对于我正在尝试做的事情来说有点复杂。我只是在寻找最简单的方法来分解上面的三个数据包并访问每个数据包的内容。某种基于正则表达式的嵌套拆分可以解决问题?
  • 我建议重新命名这个问题 - 我已经忽略了一段时间,因为它特定于文件上传和事情,而它似乎是解析的一般问题。我已经重新标记以表明这一点,但明确说明它是关于编写解析器的标题可能会吸引更多回复。
  • 你说得很好,谢谢@submonoid。我改变了标题。

标签: parsing scala


【解决方案1】:

我很好奇你的“特别慢”实际上有多慢。我编写了以下简单的小函数来生成假消息:

def generateFakeMessage(n: Int) = {
  val rand = new scala.util.Random(1L)
  val maxLines = 100
  val maxLength = 100

  (1 to n).map(i =>
    "--*****org.apache.cordova.formBoundary\n" +
    "Content-Disposition: form-data; name=\"value%d\"\n\n".format(i) +
    (0 to rand.nextInt(maxLines)).map(_ =>
      (0 to rand.nextInt(maxLength)).map(_ => rand.nextPrintableChar).mkString
    ).mkString("\n")
  ).mkString("\n") + "\n--*****org.apache.cordova.formBoundary--"
}

接下来我创建了一个相当大的消息用于测试:

val data = generateFakeMessage(10000)

它最终包含超过 50 万行。然后我尝试了你的正则表达式:

data.split("\\Q--*****org.apache.cordova.formBoundary\\E").size

而且它或多或少会立即返回。您可能可以稍微调整正则表达式,如果您的数据是消息行中的Iterable[String],您可以使用更简洁的方法,但我认为您不会从手上获得更好的性能-滚动状态机用于解析一个大的String

【讨论】:

  • 谢谢特拉维斯。我很傻,将我的代码测试为 Scala 脚本。我没有意识到它一直在招致启动惩罚。看到您的代码执行速度有多快,实际上是非常令人印象深刻的。我保持这个问题是开放的,希望有人能就我的解析问题给我指导。希望这没问题。
【解决方案2】:

对于第一个建议,this question 给出了两个建议,一个使用状态机,另一个使用解析器组合器。我会特别注意使用解析器组合器的答案,因为它们提供了一种非常简单的方法来构建这种解析器。 Daniel 的回答中提供的语法应该很容易适应您的情况。

此外,如果需要,您可以为您的特定语法提供更具体的 Scala 映射。丹尼尔在哪里:

def field = (fieldName name -> body }

您可以将其替换为多个字段 (contentType|contentDisposition|....) 上的交替模式,并将每个字段单独映射到您的 Scala 对象中。

很抱歉没有时间在这里写一个更详细的解决方案,但这应该会为您指明正确的方向!

【讨论】:

  • 我以前没有使用过解析器组合器,但我想现在是个好时机;-) 在问了这个问题后,我确实偶然发现了这个解决方案,但这个问题的约束是不想将整个文件加载到内存中。我没有这个限制,所以认为可能有更好的方法。我真的在寻找一个简单的解决方案,因为我知道一定有一个。与此同时,我会再看一遍,谢谢,谢谢你的建议。我只是想确定我已经探索了所有途径,因为我需要在即将到来的项目中进行大量解析。
  • 对于一个非常简单的解决方案,您的拆分方法可能没问题。组合器派上用场的地方是从非常简单的解析器构建复杂的解析器。虽然 Daniel 的回答中给出的内容可能看起来很复杂,但每个单独的部分都非常简单,因此您可以从片段构建一个解析器,而不是试图一次了解整个语法。
【解决方案3】:

我认为你的解决方案:

data.split("\\Q--*****org.apache.cordova.formBoundary\\E") foreach println

复杂度为 O(n),是你能得到的最好和最简单的。正如特拉维斯之前所说,这种操纵并不慢。与多部分 HTTP 表单一样,您必须以一种或另一种方式解析它,并且对 O(n) 做得更好似乎很棘手。

此外,split 为您提供Iterable 它非常适合任何匹配、治疗...

【讨论】:

    【解决方案4】:

    这可能是最糟糕的解决方案,并且无法以任何方式扩展,但为了快速从多部分请求中获取图像数据,我执行了以下操作(如果有人给出更好的答案,我将取消标记我的答案):

    // Take the request and split it into parts
    var requestParts = request.content.toString(UTF_8).split("\\Q--*****org.apache.cordova.formBoundary\\E")
    // Split the third part at the blank line
    val imageParts = requestParts(3).split("\\n\\s*\\n")
    // The part above the blank line is the header text
    val imageHeader = imageParts(0)
    // The part below the blank line is the image body
    val imageBodyString = imageParts(1)
    

    我稍后会尝试改进这一点,但现在必须继续推进。改天,另一个项目:-o

    【讨论】:

      猜你喜欢
      • 2016-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-07
      • 2016-01-12
      相关资源
      最近更新 更多