All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fm.common.FileUtil.scala Maven / Gradle / Ivy

/*
 * Copyright 2014 Frugal Mechanic (http://frugalmechanic.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fm.common

import java.io._
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.{Files, StandardCopyOption}

object FileUtil extends Logging {
  def md5(f: File)    : Array[Byte] = Resource.using(new FileInputStream(f)){ DigestUtils.md5(_) }
  def md5Hex(f: File) : String      = Resource.using(new FileInputStream(f)){ DigestUtils.md5Hex(_) }
  def sha1(f: File)   : Array[Byte] = Resource.using(new FileInputStream(f)){ DigestUtils.sha1(_) }
  def sha1Hex(f: File): String      = Resource.using(new FileInputStream(f)){ DigestUtils.sha1Hex(_) }

  def detectCharset(f: File): Option[Charset] = InputStreamResource.forFileOrResource(f).detectCharset()
  def detectCharsetName(f: File): Option[String] = InputStreamResource.forFileOrResource(f).detectCharsetName()

  def getDirectoryForFile(f: File): File = {
    val tmp: File = if (f.isDirectory) f else f.getParentFile
    assert(tmp.isDirectory)
    tmp
  }

  def writeRawFile(f: File, overwrite: Boolean)(fun: OutputStream => Unit): Unit = {
    FileOutputStreamResource(f, overwrite = overwrite, autoCompress = false).use(fun)
  }
  
  def writeRawFile(f: File, is: InputStream, overwrite: Boolean): Unit = writeRawFile(f, overwrite){ (os: OutputStream) =>
    IOUtils.copy(is, os)
  }
  
  def writeFile[T](f: File, overwrite: Boolean)(fun: OutputStream => T): T = {
    FileOutputStreamResource(f, overwrite = overwrite).use(fun)
  }

  def writeFile(f: File, contents: String, overwrite: Boolean): Unit = {
    writeFile(f, contents.getBytes(UTF_8), overwrite)
  }
  
  def writeFile(f: File, bytes: Array[Byte], overwrite: Boolean): Unit = {
    writeFile(f, overwrite) { (os: OutputStream) =>
      os.write(bytes)
    }
  }
  
  def writeFile(f: File, is: InputStream, overwrite: Boolean): Unit = writeFile(f, overwrite){ (os: OutputStream) =>
    IOUtils.copy(is, os)
  }
  
  /**
   * Creates a tmp file that can be written to and then will be atomically renamed to the target
   */
  def writeFileWithTemp[T](target: File)(f: File => T): T = {
    val tmp: File = File.createTempFile(".fm_tmp", target.getName, getDirectoryForFile(target))
    
    try {
      val res: T = f(tmp)
      Files.move(tmp.toPath, target.toPath, StandardCopyOption.ATOMIC_MOVE)
      res
    } catch {
      case ex: Throwable =>
        if (tmp.isFile) tmp.delete()
        throw ex
    }
  }

  /**
   * Creates a temp file, passes it to the function, and then deletes the temp file
   */
  def withTempFile[T](prefix: String, suffix: String)(f: File => T): T = {
    useFileThenDelete(File.createTempFile(prefix, suffix))(f)
  }

  /**
   * Creates a temp file, passes it to the function, and then deletes the temp file
   */
  def withTempFile[T](prefix: String, suffix: String, directory: File)(f: File => T): T = {
    useFileThenDelete(File.createTempFile(prefix, suffix, directory))(f)
  }

  /**
   * Passes the file into the passed in function and then delete the file
   */
  def useFileThenDelete[T](file: File)(f: File => T): T = {
    try {
      f(file)
    } finally {
      if (file.isFile) file.delete()
    }
  }
  
  def copy(src: File, dst: File, overwrite: Boolean = true): Unit = {
    def bothEndWith(s: String): Boolean = src.getName.endsWith(s) && dst.getName.endsWith(s)
    
    // Only enable autoDecompress/autoCompress if the compression formats don't already match 
    val compression: Boolean = if (bothEndWith(".gz") || bothEndWith(".zip") || bothEndWith(".snappy")) false else true
    
    InputStreamResource.forFile(src, autoDecompress = compression).use { is =>
      FileOutputStreamResource(dst, autoCompress = compression).use { os =>
        IOUtils.copy(is, os)
      }
    }
  }
  
  def fileExists(file: String): Boolean = fileExists(new File(file))
  def fileExists(file: File): Boolean = file.isFile
  
  def resourceExists(file: String): Boolean = ClassUtil.classpathFileExists(file)
  def resourceExists(file: File): Boolean = ClassUtil.classpathFileExists(file)
  
  def fileOrResourceExists(file: String): Boolean = fileExists(file) || resourceExists(file)
  def fileOrResourceExists(file: File): Boolean = fileExists(file) || resourceExists(file)

  def fileOrResourceLastModified(file: String): Long = fileOrResourceLastModified(file.toFile)

  def fileOrResourceLastModified(file: File): Long = {
    if (file.isFile && file.canRead) file.lastModified() else ClassUtil.classpathLastModified(file)
  }

  def readFile(file: String): String = readFile(file, UTF_8)
  def readFile(file: String, encoding: String): String = readFile(new File(file), encoding)
  def readFile(file: String, encoding: Charset): String = readFile(new File(file), encoding)

  def readFile(f: File): String = readFile(f, UTF_8)
  def readFile(f: File, encoding: String): String = InputStreamResource.forFile(f).readToString(encoding)
  def readFile(f: File, encoding: Charset): String = InputStreamResource.forFile(f).readToString(encoding)

  def readResource(file: String): String = readResource(file, UTF_8)
  def readResource(file: String, encoding: String): String = readResource(new File(file), encoding)
  def readResource(file: String, encoding: Charset): String = readResource(new File(file), encoding)

  def readResource(f: File): String = readResource(f, UTF_8)
  def readResource(f: File, encoding: String): String = InputStreamResource.forResource(f).readToString(encoding)
  def readResource(f: File, encoding: Charset): String = InputStreamResource.forResource(f).readToString(encoding)

  def readFileOrResource(file: String): String = readFileOrResource(file, UTF_8)
  def readFileOrResource(file: String, encoding: String): String = readFileOrResource(new File(file), encoding)
  def readFileOrResource(file: String, encoding: Charset): String = readFileOrResource(new File(file), encoding)

  def readFileOrResource(f: File): String = readFileOrResource(f, UTF_8)

  def readFileOrResource(f: File, encoding: String): String = {
    InputStreamResource.forFileOrResource(f).readToString(encoding)
  }

  def readFileOrResource(f: File, encoding: Charset): String = {
    InputStreamResource.forFileOrResource(f).readToString(encoding)
  }
  
  def readLines(file: File)(f: String => Unit): Unit = {
    readLines(InputStreamResource.forFile(file).bufferedReader())(f)
  }
  
  def readLines(is: InputStream)(f: String => Unit): Unit = {
    readLines(InputStreamResource.forInputStream(is).bufferedReader())(f)
  }
  
  def readLines(resource: Resource[BufferedReader])(f: String => Unit): Unit = resource.use{ (reader: BufferedReader) =>
    var line: String = reader.readLine
    while (null != line) {
      f(line)
      line = reader.readLine
    }
  }

  def readBytes(file: String): Array[Byte] = readBytes(new File(file))

  def readBytes(f: File): Array[Byte] = InputStreamResource.forFile(f).readBytes()

  def readInputStream(is: InputStream): String = readInputStream(is, UTF_8)
  
  def readInputStream(is: InputStream, encoding: String): String = readInputStream(is, CharsetUtil.forName(encoding))
  
  def readInputStream(is: InputStream, charset: Charset): String = {
    val reader: BufferedReader = new BufferedReader(new InputStreamReader(is, charset))
    val writer: StringWriter = new StringWriter()

    IOUtils.copy(reader, writer)
    reader.close()
    writer.toString
  }

  def rm_rf(dir: File): Boolean = rm_rf(dir, false)

  def rm_rf(dir: File, keepDirectory: Boolean): Boolean = {
    logger.warn("rm -rf " + dir.getAbsolutePath)
    if (dir.isDirectory) {
      val children: Array[String] = dir.list

      children.foreach { (child: String) =>
        if (!rm_rf(new File(dir, child))) return false
      }

      if (!keepDirectory) dir.delete() else true
    } else {
      val success: Boolean = dir.delete()
      if (!success) logger.warn(s"File deletion for ${dir.getAbsolutePath} failed")
      success
    }
  }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy