2012-03-19 21 views
11

का उपयोग कर फ़ाइल अपलोड सामग्री के साथ मल्टीपार्ट HTTP फॉर्म डेटा पार्सिंग multipart/form-data फ़ाइल अपलोड समाधान बहुत सारे हैं, लेकिन मैं स्कैला के लिए एक नि: शुल्क खड़ा नहीं ढूंढ पा रहा हूं।स्केल

Play2 में फ्रेमवर्क के हिस्से के रूप में यह कार्यक्षमता है और स्प्रे मल्टीपार्ट फॉर्म डेटा का भी समर्थन करता है। दुर्भाग्य से ये दोनों शेष टूलसेट में काफी एकीकृत होते हैं (मैं यहां गलत हो सकता हूं)।

मेरा सर्वर फिनगेल (जो वर्तमान में मल्टीपार्ट फॉर्म डेटा का समर्थन नहीं करता है) का उपयोग करके विकसित किया गया है, और यदि संभव हो तो मैं एक मुफ्त स्थायी 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 पाठ मापदंडों और एक छवि है (मैं के लिए छवि डेटा concatenated स्पष्टता)।

यदि कोई व्यक्ति जो मेरे से बेहतर स्कैला जानता है, तो मुझे इस सामग्री को पार्स करने के तरीके पर एक रैंड डाउन दे सकता है, मैं बहुत आभारी रहूंगा।

शुरू में, मैंने सोचा कि मैं जल्दी से कर रही तीन में सामग्री विभाजित होगा:

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

लेकिन निष्पादन विशेष रूप से धीमी गति से (अद्यतन - इस समय को गर्म करने के कारण किया गया था) है। क्या भागों को विभाजित करने का एक और अधिक प्रभावी तरीका है? मेरी रणनीति सामग्री को भागों में विभाजित करना है, और भागों को उप-भागों में विभाजित करना है। क्या यह एक गड़बड़ दृष्टिकोण है? मैंने राज्य मशीनों के साथ हल की जाने वाली समान समस्याओं को देखा है? एक अच्छा कार्यात्मक दृष्टिकोण क्या है। ध्यान रखें, समस्या को हल करने की कोशिश करते समय मैं स्कैला के लिए एक उचित दृष्टिकोण सीखने की कोशिश कर रहा हूं।

अद्यतन:

मैं वास्तव में सोचा कि यह समस्या का समाधान एक पंक्ति या दो स्काला में होगा। अगर कोई इस सवाल पर एक स्लिम समाधान के साथ ठोकर खाता है, तो कृपया इसे कम करने के लिए समय दें। मेरी समझ से कोई पैटर्न मिलान, पार्सिंग संयोजक, निष्कर्षण या बस स्ट्रिंग को विभाजित करके इस संदेश को पार्स कर सकता है। मैं इस तरह की समस्या को हल करने का सबसे अच्छा तरीका खोजने की कोशिश कर रहा हूं, क्योंकि एक परियोजना जिसमें मैं काम कर रहा हूं, में बहुत सारी प्राकृतिक भाषा पार्सिंग शामिल है, और मुझे अपना खुद का कस्टम पार्सिंग टूल लिखना है। मुझे स्कैला की अच्छी समझ मिल रही है, लेकिन कुछ भी विशेषज्ञ की सलाह नहीं मानता है।

यह समस्या को हल करने के बारे में नहीं है, यह इस प्रकार की समस्या को हल करने के लिए सबसे अच्छा (और उम्मीदवार सरलतम) संभव तरीका खोजने के बारे में है।

+0

आप यहां प्ले कोड पा सकते हैं https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/ mvc/ContentTypes.scala यह उचित समझ में आता है –

+0

धन्यवाद @ पॉल। मैंने प्ले कोड पर एक नज़र डाली, लिंक के लिए धन्यवाद। मैं इसे सबसे अधिक समझता हूं, हालांकि, मैं जो कुछ करने की कोशिश कर रहा हूं उसके लिए यह थोड़ा जटिल है। मैं बस ऊपर दिए गए तीन डेटा पैकेट को अलग करने और प्रत्येक पैकेट की सामग्री तक पहुंचने का सबसे आसान तरीका ढूंढ रहा हूं। नियमित अभिव्यक्तियों के आधार पर घोंसला वाले विभाजन के कुछ प्रकार चाल चल सकते हैं? – Jack

+0

मैं इस प्रश्न को फिर से लिखने का सुझाव दूंगा - मैंने फ़ाइल अपलोड और चीजों के लिए विशिष्ट होने के नाते इसे थोड़ी देर के लिए अनदेखा कर दिया है, जबकि यह पार्सिंग पर एक सामान्य प्रश्न प्रतीत होता है। मैंने इसे इंगित करने के लिए पुनः प्रयास किया है, लेकिन एक शीर्षक जो इसे एक पार्सर लिखने के बारे में स्पष्ट करता है, शायद अधिक जवाब आकर्षित करेगा। – 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) 

मैं बाद में इस पर सुधार करने की कोशिश करेंगे, लेकिन अब के लिए आगे पुश करने के लिए है। एक और दिन, एक और परियोजना: -o

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] पर क्लीनर दृष्टिकोण का उपयोग कर सकता है, लेकिन मुझे नहीं लगता कि आपको हाथ से लुढ़कने से बेहतर प्रदर्शन करने जा रहे हैं एक बड़ी String पार्सिंग के लिए राज्य मशीन।

+0

धन्यवाद ट्रेविस। मैं मूर्खतापूर्ण था और स्कैला लिपि के रूप में मेरे कोड का परीक्षण किया। मुझे एहसास नहीं हुआ कि यह स्टार्ट-अप जुर्माना लगा रहा है। यह वास्तव में बहुत प्रभावशाली है कि आपका कोड कितना तेज़ निष्पादित करता है। मैं उम्मीद में सवाल खोल रहा हूं कि कोई मुझे मेरी पार्सिंग समस्या पर मार्गदर्शन देगा। उम्मीद है कि यह ठीक है। – Jack

0

पहले सुझाव के लिए, this question दो सुझाव देता है, एक राज्य मशीन का उपयोग करके, और दूसरा पार्सर संयोजक का उपयोग करता है। मैं पार्सर combinators का उपयोग कर जवाब पर विशेष ध्यान देना होगा, क्योंकि यह इस तरह के पार्सर बनाने के लिए एक बहुत ही आसान तरीका प्रदान करते हैं। डैनियल के जवाब में दिए गए वाक्यविन्यास को आपकी स्थिति में बहुत आसानी से अनुकूलित करना चाहिए।

आगे, यदि आप की आवश्यकता हो तो आप अपने विशेष व्याकरण के लिए स्कैला में अधिक विशिष्ट मैपिंग प्रदान कर सकते हैं। कहाँ डैनियल है:

डीईएफ़ क्षेत्र = (FIELDNAME < ~ ":") ~ fieldBody < ~ CRLF ^^ {मामले नाम ~ शरीर => नाम -> शरीर}

आप इस जगह ले सकता है एकाधिक क्षेत्रों (contentType|contentDisposition|....) पर एक वैकल्पिक पैटर्न के साथ और इनमें से प्रत्येक को अलग-अलग अपने स्कैला ऑब्जेक्ट्स में मैप करें।

यहां अधिक विस्तृत समाधान लिखने का समय न मिलने के लिए क्षमा करें, लेकिन उम्मीद है कि आपको सही दिशा में इंगित करना चाहिए!

+0

मैंने पहले पार्सर संयोजक का उपयोग नहीं किया है, लेकिन अब किसी भी समय के रूप में अच्छा है, मुझे लगता है ;-) मैं इस सवाल पूछने के बाद उस समाधान पर ठोकर खाई, लेकिन सवाल में पूरी फाइल लोड नहीं करना चाहते थे स्मृति में मेरे पास यह बाधा नहीं है, इसलिए सोचा कि एक बेहतर तरीका हो सकता है। मैं वास्तव में एक साधारण समाधान की तलाश में हूं, क्योंकि मुझे पता है कि वहां एक होना चाहिए। इस बीच, मैं इसे एक और रूप दूंगा, धन्यवाद, और सलाह के लिए धन्यवाद। मैं बस यह सुनिश्चित करना चाहता हूं कि मैंने सभी रास्ते खोजे हैं, क्योंकि मुझे आगामी परियोजना में बहुत सी पार्सिंग करने की ज़रूरत है। – Jack

+0

वास्तव में एक सरल समाधान के लिए, आप शायद अपनी विभाजन विधि के साथ ठीक हैं। जहां संयोजक काम में आते हैं, जटिल पार्सर्स को बहुत सरल से बनाते हैं। जबकि डैनियल के जवाब में दिए गए लोग जटिल लग सकते हैं, प्रत्येक व्यक्ति का हिस्सा बहुत ही सरल है, इसलिए आप एक बार में पूरे व्याकरण को पकड़ने की बजाय टुकड़ों से एक पार्सर तक बना सकते हैं। – Submonoid

0

मुझे लगता है कि अपने समाधान:

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

जो जटिलता में हे है (एन), सबसे अच्छा और सरल आप प्राप्त कर सकते है। जैसा कि ट्रैविस ने पहले कहा था, यह हेरफेर धीमा नहीं है। हमेशा एक मल्टीपार्ट HTTP फॉर्म के साथ, आपको इसे एक या दूसरे तरीके से पार्स करना होगा और ओ (एन) को बेहतर करना मुश्किल लगता है।

इसके अलावा, के रूप में split आप एक Iterable यह किसी भी मिलान, इलाज के लिए वास्तव में एकदम सही है प्रदान करता है ...