playframework - Scala: Read some data of an Enumerator[T] and return the remaining Enumerator[T] -
i using asynchronous i/o library of playframework uses iteratees , enumerators. have iterator[t] data sink (for simplification it's iterator[byte] stores content file). iterator[byte] passed function handles writing.
but before writing want add statistical information @ file begin (for simplification it's 1 byte), transfer iterator following way before passing write function:
def write(value: byte, output: iteratee[byte]): iteratee[byte] = iteratee.flatten(output.feed(input.el(value)))
when read stored file disk, enumerator[byte] it. @ first want read , remove additional data , want pass rest of enumerator[byte] function handles reading. need transform enumerator:
def read(input: enumerator[byte]): (byte, enumerator[byte]) = { val firstenumeratorentry = ... val remainingenumerator = ... (firstenumeratorentry, remainingenumerator) }
but have no idea, how this. how can read bytes enumerator , remaining enumerator?
replacing iteratee[byte] outputstream , enumerator[byte] inputstream, easy:
def write(value: byte, output: outputstream) = { output.write(value) output } def read(input: inputstream) = (input.read,input)
but need asynchronous i/o of play framework.
here 1 way achieve folding within iteratee
, appropriate (kind-of) state accumulator (a tuple here)
i go read routes
file, first byte read char
, other appended string
utf-8 bytestrings.
def index = action { /*let's asyncly*/ async { /*for comprehension read-friendly*/ ( <- read; /*read file */ (r:(option[char], string)) <- i.run /*"create" related promise , run it*/ ) yield ok("first : " + r._1.get + "\n" + "rest" + r._2) /* map promised result in correct request's result*/ } } def read = { //get routes file in enumerator val file: enumerator[array[byte]] = enumerator.fromfile(play.getfile("/conf/routes")) //apply enumerator iteratee folds data wished file(iteratee.fold((none, ""):(option[char], string)) { (acc, b) => acc._1 match { /*on first chunk*/ case none => (some(b(0).tochar), acc._2 + new string(b.tail, charset.forname("utf-8"))) /*on other chunks*/ case x => (x, acc._2 + new string(b, charset.forname("utf-8"))) } }) }
edit
i found yet way using enumeratee
needs create 2 enumerator
s (one short lived). bit more elegant. use "kind-of" enumeratee traversal
1 works @ finer level enumeratee (chunck level). use take
1 take 1 byte , close stream. on other one, use drop
drops first byte (because we're using enumerator[array[byte]])
furthermore, read2
has signature more closer wished, because returns 2 enumerators (not far promise, enumerator)
def index = action { async { val (first, rest) = read2 val enee = enumeratee.map[array[byte]] {bs => new string(bs, charset.forname("utf-8"))} def useenee(enumor:enumerator[array[byte]]) = iteratee.flatten(enumor &> enee |>> iteratee.consume[string]()).run.asinstanceof[promise[string]] { f <- useenee(first); r <- useenee(rest) } yield ok("first : " + f + "\n" + "rest" + r) } } def read2 = { def create = enumerator.fromfile(play.getfile("/conf/routes")) val file: enumerator[array[byte]] = create val file2: enumerator[array[byte]] = create (file &> traversable.take[array[byte]](1), file2 &> traversable.drop[array[byte]](1)) }
Comments
Post a Comment