2012-03-19 2 views
11

を使用してファイルのアップロードコンテンツとマルチパートHTTPフォームデータの解析multipart/form-dataファイルアップロードのソリューションの多くはそこにありますが、私はScalaのための自立ものを見つけることができませんでした。はScalaの

PLAY2は、フレームワークの一部としてこの機能を有しており、また、マルチパートフォームデータをサポートスプレー。残念ながら、これらは両方とも、残りのツールセットにかなり統合されているようです(私はここで間違っているかもしれません)。

私のサーバーは、(現在はマルチパートフォームデータをサポートしていません)Finagleを使用して開発されてきた、そして可能ならば、私は自立のlibを使用するか、またはソリューション「自分のロール」したいと思います。

これは、典型的なマルチパート/フォームデータメッセージである:この例では

--*****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をよく知っている人が、このコンテンツの解析にどのようにアプローチすればいいのか分かりませんが、とても感謝しています。で開始する

は、私はすぐにやって3でコンテンツを分割するだろうと思った:

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

をしかし、実行が著しく遅くなる(アップデート - これは時間をウォームアップによるものでした)。部品を分割する効率的な方法はありますか?私の戦略は、コンテンツを部分に分割し、部分を部分に分割することです。これは虚偽のアプローチですか?私は、同様の問題がステートマシンで解決されるのを見たことがありますか?優れた機能的アプローチとは何ですか?私は問題を解決しようとしている間、Scalaへの適切なアプローチを学ぼうとしています。

更新:

私は本当にこの問題を解決するには、行またはスカラ座で2だろうと思いました。誰かがこの問題を滑らかな解決策で見つけた場合は、時間をかけて書き留めてください。私の理解から、一つは、パターンマッチングを使用して、このメッセージを解析コンビネータを解析し、抽出または単純に文字列を分割することができます。私が働いているプロジェクトは、自然言語構文解析の多くが含まれて私は、この種の問題を解決するための最良の方法を見つけようとしている、と私は自分のカスタム解析ツールを記述する必要があります。私はScalaをよく理解していますが、何も専門家の助言に勝るものはありません。

問題を解決することだけではなく、このタイプの問題を解決するための最良の(そしてうまくいえば最も単純な)可能な方法を見つけることです。

+0

ここでプレーコードを見つけることができます。https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/ mvc/ContentTypes.scalaそれは合理的に理解できるようです –

+0

ありがとう@Paul。私は、プレイコードを見て、リンクのおかげで。私はそれのほとんどを理解していますが、それは私がやろうとしていることに対してちょっと複雑です。上記の3つのデータパケットを分割して各パケットのコンテンツにアクセスする最も簡単な方法を探しています。正規表現に基づくネストされた分割のいくつかの並べ替えは、トリックを行うことがありますか? – Jack

+0

私はこの質問をretitlingすることをお勧めします - 私はしばらくの間アップロードと物事をファイルに固有のものとして無視してきましたが、それは解析に関する一般的な質問のようです。私はこれを示すために再タグ付けしましたが、パーサを書くことに関する明確なタイトルはおそらくもっと多くの回答を引き付けるでしょう。 – Submonoid

答えて

0

これはおそらく、どのような方法で最悪の可能な解決策、およびスケーラブルではありませんが、すぐに単に画像を取得します

// 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) 

私はこれ以降で改善しようと、今のところ先にプッシュする必要があります:私は(誰かがより良い答えを与えるならば、私は私の答えをマーク解除します)以下のなかったマルチパート要求からのデータ。もう1つのプロジェクト:-0

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) 

を。次に、あなたの正規表現を試しました:

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

そして、多かれ少なかれ瞬時に返します。あなたはおそらく正規表現を少し調整することができます。データがメッセージの行の上にIterable[String]だった場合に使用できるよりきれいなアプローチがありますが、私はあなたが手巻きでより良いパフォーマンスを得るつもりはないと思います大きな1つを解析するためのステートマシンString

+0

トラヴィスありがとうございます。私は愚かで、自分のコードをScalaスクリプトとしてテストしていました。私はそれがスタートアップのペナルティを受け続けたことを理解していませんでした。コードがどれほど速く実行されるかは、実際は非常に印象的です。私は誰かが私の解析問題についてのガイダンスを与えることを願って質問を開いたままにしています。これはいいですね。 – Jack

0

最初の提案として、this questionには、1つはステートマシンを使用し、もう1つはパーサーコンビネータを使用する2つの提案を示します。私はパーサコンビネータを使って答えに特別な注意を払うでしょう。なぜなら、これらはパーサーのこのような種類を構築する非常に簡単な方法を提供するからです。ダニエルの答えで提供される構文は、あなたの状況に非常に簡単に適応する必要があります。

さらに、必要に応じて、特定の文法のために、より具体的なマッピングをScalaに提供することができます。ダニエルは有している場合:

DEFフィールドは=(fieldNameに<〜 ":")〜fieldBody <〜CRLF ^^ - あなたがこれを置き換えることができます{ケース名〜ボディ=>名>ボディ}

を複数のフィールド(contentType|contentDisposition|....)に交互にパターンを設定し、これらを個別にScalaオブジェクトにマップします。

ここで詳細な解決策を書く時間がないことをお詫びしますが、これはうまくいけば正しい方向に向かうはずです!

+0

私は以前にパーサーコンビネータを使用していませんでしたが、今のところ良いと思っています;-)私はこの問題を尋ねた後にその解決策を見つけませんでしたが、質問はファイル全体をロードしたくないという制約がありましたメモリに保存する。私はこの制約がないので、より良い方法があるかもしれないと考えました。私は本当にシンプルな解決策を探しています。なぜなら、あるべきことが分かっているからです。その間、私はもう一度見て、感謝、そしてアドバイスに感謝します。私は今後のプロジェクトで多くの解析を行う必要があるため、すべての道を模索していることを確信したいだけです。 – Jack

+0

本当に簡単な解決方法は、分割方法を使っても問題ありません。コンビネータが便利なのは、非常に単純なものから複雑なパーサを構築することです。ダニエルの答えで与えられたものは複雑に見えるかもしれませんが、個々の部分は非常に単純なので、一度に文法全体を突き詰めるのではなく、断片からパーサを構築することができます。 – Submonoid

0

私はあなたの解決策だと思う:

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

複雑でO(n)は、あなたが得ることができる最高と最も簡単です。 Travisが以前に言ったように、この操作は遅くない。いつものようにマルチパートのHTTPフォームでは、あなたはそれを何らかの方法で解析しなければならず、O(n)をうまくやっていくのは難しいようです。また

splitは、それがどんなマッチング、治療のために本当に完璧ですIterableはあなたに提供しては...