Как я могу прочитать файл в InputStream, а затем записать его в OutputStream в Scala?

Я пытаюсь использовать базовый код Java в Scala для чтения из файла и записи в OutputStream, но когда я использую обычный while (! = -1) в Scala дает мне предупреждение: «сравнение типов Unit и Int с! = Всегда даст true».

Код выглядит следующим образом:

    val file = this.cache.get(imageFileEntry).getValue().asInstanceOf[File]
    response.setContentType( "image/%s".format( imageDescription.getFormat() ) )

    val input = new BufferedInputStream( new FileInputStream( file ) )
    val output = response.getOutputStream()

    var read : Int = -1

    while ( ( read = input.read ) != -1 ) {
        output.write( read )
    }

    input.close()
    output.flush()

Как я должен писать из входного потока в выходной поток в Scala?

Меня больше всего интересует решение типа Scala.


person Maurício Linhares    schedule 03.08.2011    source источник
comment
С точки зрения производительности было бы неплохо использовать промежуточный буфер вместо чтения и записи по одному байту за раз.   -  person Knut Arne Vedaa    schedule 04.08.2011
comment
Вот почему там есть BufferedInputStream.   -  person Maurício Linhares    schedule 26.10.2012


Ответы (5)


Вы могли сделать это:

Iterator 
.continually (input.read)
.takeWhile (-1 !=)
.foreach (output.write)
person Daniel C. Sobral    schedule 03.08.2011
comment
Хорошо, это похоже на настоящее решение Scala, но этот вызов постоянно загружает файл в память или будет вызывать эту функцию во время выполнения цикла foreach? Кроме того, не могли бы вы подробнее рассказать об этом методе takeWhile? Почему вам не пришлось использовать параметр _ или определять параметр самостоятельно? - person Maurício Linhares; 03.08.2011
comment
@ Maurício Это итератор, поэтому все делается только по запросу. До foreach ничего особенного не происходит - вы просто получаете новые Iterator объекты, которые выполняют некоторую предварительную обработку перед next или hasNext. На foreach output.write выполняется для каждого input.read, а затем его значение быстро забывается и собирается мусор. - person Daniel C. Sobral; 04.08.2011
comment
Было бы неплохо иметь версию, использующую проект инкубатора scala-io - person gerferra; 06.08.2011
comment
Следует отметить, что для максимальной производительности как входной, так и выходной поток должны быть буферизованы (с использованием Buffered {Input, Output} Stream). Он по-прежнему будет значительно медленнее, чем не в стиле Scala - person lyomi; 22.06.2017

Если это медленно:

Iterator 
.continually (input.read)
.takeWhile (-1 !=)
.foreach (output.write)

вы можете расширить его:

val bytes = new Array[Byte](1024) //1024 bytes - Buffer size
Iterator
.continually (input.read(bytes))
.takeWhile (-1 !=)
.foreach (read=>output.write(bytes,0,read))
output.close()
person Yaroslav    schedule 30.05.2013
comment
Это предполагает, что вы в порядке, просто читаете 1024 байта. Что делать, если я не знаю, сколько мне нужно читать, пока не дойду до чего-то вроде разграничителя? - person foxtrotuniform6969; 13.07.2019

Операторы присваивания всегда возвращают Unit в Scala, поэтому read = input.read возвращает Unit, который никогда не равен -1. Сделать это можно так:

while ({read = input.read; read != -1}) {
  output.write(read)
}
person Kim Stebel    schedule 03.08.2011
comment
У вас не может быть более одного оператора в предложении while / if / for. Этот код дает ошибку компиляции. Но спасибо за задание, я думал, что он будет вести себя как java. - person Maurício Linhares; 03.08.2011
comment
Привет, Маурисио! Да, {} являются ключевым моментом в его примере. Назовем их блоком. Все внутри будет выполнено, и в конечном итоге будет возвращен результат с типом последнего выражения. - person AndreasScheinert; 03.08.2011
comment
извините, добавили подтяжки немного поздно;) - person Kim Stebel; 03.08.2011

Мы можем скопировать входной поток в выходной поток обычным и безопасным для типов способом, используя классы типов. Класс типов - это концепция. Это один из подходов к полиморфизму. В частности, это параметрический полиморфизм, потому что полиморфное поведение кодируется с использованием параметров. В нашем случае наши параметры будут универсальными типами для свойств Scala.

Создадим черты Reader[I] и Writer[O], где I и O - это типы входного и выходного потоков соответственно.

trait Reader[I] {
  def read(input: I, buffer: Array[Byte]): Int
}

trait Writer[O] {
  def write(output: O, buffer: Array[Byte], startAt: Int, nBytesToWrite: Int): Unit
}

Теперь мы можем создать общий метод копирования, который может работать с вещами, которые подписываются на эти интерфейсы.

object CopyStreams {

  type Bytes = Int

  def apply[I, O](input: I, output: O, chunkSize: Bytes = 1024)(implicit r: Reader[I], w: Writer[O]): Unit = {
    val buffer = Array.ofDim[Byte](chunkSize)
    var count = -1

    while ({count = r.read(input, buffer); count > 0})
      w.write(output, buffer, 0, count)
  }
}

Обратите внимание на неявные параметры r и w. По сути, мы говорим, что CopyStreams[I,O].apply будет работать, если в области видимости есть значения Reader[I] и Writer[O]. Это позволит нам беспрепятственно вызывать CopyStreams (ввод, вывод).

Однако важно отметить, что эта реализация является общей. Он работает с типами, которые не зависят от фактических реализаций потоков.

В моем конкретном случае использования мне нужно было скопировать объекты S3 в локальные файлы. Итак, я сделал следующие неявные значения.

object Reader {

  implicit val s3ObjectISReader = new Reader[S3ObjectInputStream] {
    @inline override def read(input: S3ObjectInputStream, buffer: Array[Byte]): Int =
      input.read(buffer)
  }
}


object Writer {

  implicit val fileOSWriter = new Writer[FileOutputStream] {
    @inline override def write(output: FileOutputStream,
                               buffer: Array[Byte],
                               startAt: Int,
                               nBytesToWrite: Int): Unit =
      output.write(buffer, startAt, nBytesToWrite)
  }
}

Итак, теперь я могу сделать следующее:

val input:S3ObjectStream = ...
val output = new FileOutputStream(new File(...))
import Reader._
import Writer._
CopyStreams(input, output)
// close and such...

И если нам когда-нибудь понадобится скопировать разные типы потоков, нам нужно только написать новое Reader или Writer неявное значение. Мы можем использовать CopyStreams код, не меняя его!

person Malcolm    schedule 13.04.2015

person    schedule
comment
Это лучшее решение. Вышеупомянутые решения ужасно медленные. Один вызов на чтение байта накладных расходов? Действительно? Насколько это приемлемо в средстве перемещения больших объемов данных? - person Christopher; 24.07.2012