Skip to main content
Version: 2.x

File Connector

Setup​

libraryDependencies += "dev.zio" %% "zio-connect-file" % "0.4.4"

How to use it ?​

All available FileConnector combinators and operations are available in the package object zio.connect.file, you only need to import zio.connect.file._

For instance the code below uses at each step a FileConnector operator.

import zio.connect.file._

val stream: ZStream[Any, Nothing, Byte] = ???
val sink: ZSink[Any, Nothing, Byte, Nothing, Unit] = ???

for {
dir <- tempDirPath
path <- tempPathIn(dir)
fileExists <- ZStream.fromZIO((ZStream(path) >>> existsPath(file)).tap(a => ZIO.debug(s"$path exists? $a")))
_ <- ZStream.fromZIO(stream >>> writePath(file))
_ <- ZStream.fromZIO(readPath(file) >>> sink)
} yield fileExists
  • tempDirPath - creates a directory and returns a ZStream with a single Path value, it will delete it once the for comprehension completes
  • tempPathIn - creates a file in the given directory and returns a ZStream with a single Path value, it will delete it once the for comprehension completes
  • existsPath - creates a ZStream with a single value
  • writePath - creates ZSink for writing to given file
  • readPath - creates a ZStream for reading from given file

Test / Stub​

A stub implementation of FileConnector is provided for testing purposes via the TestFileConnector.layer. It uses internally an in memory filesystem to avoid the actual creation of files.

package zio.connect.file

import zio.Scope

object MyTestSpec extends ZIOSpecDefault{

override def spec =
suite("MyTestSpec")(???)
.provide(zio.connect.file.fileConnectorTestLayer)

}

Operators & Examples​

readX​

  • readFile | readFileName | readPath | readURI

Creates a ZStream for reading a file's content from a path/file/... The stream ends once all content is read.

import zio.connect.file._

def example(path: Path): ZStream[Any, IOException, Byte] =
readPath(path)

writeX​

  • writeFile | writeFileName | writePath | writeURI

Creates a ZSink for writing to a file. The stream ends once all content is read.

import zio.connect.file._

def example(path: Path): ZSink[Any, IOException, Byte, Nothing, Unit] =
writePath(path)

tailX​

  • tailFile | tailFileName | tailPath | tailURI

Creates a ZStream for reading a file's content from a path/file/... The stream never ends emitting; it will keep polling the file (with given frequency) even after all content is read.

import zio.connect.file._

def example(path: Path, freq: Duration): ZStream[Any, IOException, Byte] =
tailPath(path, freq)

tailXUsingWatchService​

  • tailFileUsingWatchService | tailFileNameUsingWatchService | tailPathUsingWatchService | tailURIUsingWatchService

Creates a ZStream for reading a file's content from a path/file/... The stream never ends emitting; it will keep polling the file (with given frequency and if watchService detects changes) even after all content is read.

import zio.connect.file._

def example(path: Path, freq: Duration): ZStream[Any, IOException, Byte] =
tailPathUsingWatchService(path, freq)

deleteX​

  • deleteFile | deleteFileName | deletePath | deleteURI

It provides a sink that deletes the file or directory. To delete non empty directories use the deleteRecursivelyX variants.

import zio.connect.file._

def example(paths: ZStream[Any, Nothing, Path]) =
paths >>> deletePath

deleteXRecursively​

  • deleteFileRecursively | deleteFileNameRecursively | deletePathRecursively | deleteURIRecursively

Same as deleteX operator + it can delete non empty directories.

existsX​

  • existsFile | existsFileName | existsPath | existsURI

Creates a ZSink that emits true if the first path/file/... exists or a false otherwise. The files received after the first one can be found in the sink's leftovers.

import zio.connect.file._

def example(stream: ZStream[Any, IOException, Path]): ZIO[Any, IOException, Boolean] =
stream >>> existsPath

listX​

  • listFile | listFileName | listPath | listURI

Returns the files inside the given path/file/.... Fails if provided path is not a dir.

import zio.connect.file._

def example(path: Path): ZStream[Any, IOException, Path] =
listPath(path)

moveX​

  • moveFile | moveFileName | movePath | moveURI

You can provide a function Path => Path and you will get a Sink that when given a path p1 will call the function with p1 and so get a p2, then move the file/dir at p1 to p2.

import zio.connect.file._

def example(locator: Path => Path): ZSink[Any, IOException, Path, Nothing, Unit] =
movePath(locator)

moveXZIO​

  • moveFileZIO | moveFileNameZIO | movePathZIO | moveURIZIO

Same as moveX except determining the destination is effectful.

import zio.connect.file._

def example(locator: Path => ZIO[Any, IOException, Path]): ZSink[Any, IOException, Path, Nothing, Unit] =
movePathZIO(locator)

tempX / tempXIn / tempDirX / tempDirXIn​

  • tempFile | tempFileName | tempPath | tempURI
  • tempFileIn | tempFileNameIn | tempPathIn | tempURIIn
  • tempDirFile | tempDirFileName | tempDirPath | tempDirURI
  • tempDirFileIn | tempDirFileNameIn | tempDirPathIn | tempDirURIIn

With this set of operators we can create temporary files and directories that will be cleaned up automatically once the effect using them completes.

The below example creates the following structure:

  • file
  • baseDir
    • subDir
      • fileInSubDir
    • fileInBaseDir
import zio.connect.file._

def example() =
for {
file <- tempPath
baseDir <- tempDirPath
subDir <- tempDirPathIn(baseDir)
fileInBaseDir <- tempPathIn(baseDir)
fileInSubDir <- tempPathIn(subDir)
} yield ()