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

io.youi.upload.UploadManager.scala Maven / Gradle / Ivy

package io.youi.upload

import java.io.File

import io.youi.http.content.{Content, FormDataContent}
import io.youi.http.{HttpConnection, HttpStatus}
import io.youi.net._
import io.youi.server.handler.HttpHandler
import io.youi.stream.IO
import reactify.Channel

import scala.concurrent.Future
import scribe.Execution.global

/**
  * UploadManager represents a system to support uploads to the server and should be coupled with the Scala.js
  * UploadManager counter-part.
  *
  * Must be instantiated and added as an `HttpHandler` on the server and have the same upload path as the Scala.js
  * instance.
  *
  * @param path the path to receive uploads
  * @param directory the directory to store uploads to
  * @param authenticator an optional authenticator to determine uploads to accept or reject - default accepts anything
  */
case class UploadManager(path: Path = path"/upload",
                         directory: File = new File(System.getProperty("java.io.tmpdir")),
                         authenticator: Option[HttpConnection => Boolean] = None) extends HttpHandler {
  val received: Channel[UploadedFile] = Channel[UploadedFile]

  private def extensionFromFileName(fileName: String): String = {
    val lastDotIndex = fileName.lastIndexOf('.')
    if (lastDotIndex != -1) {
      fileName.substring(lastDotIndex + 1)
    } else {
      "bin"
    }
  }

  override def handle(connection: HttpConnection): Future[HttpConnection] = if (connection.request.url.path == path) {
    Future {
      val authenticated = authenticator.forall(_(connection))
      if (authenticated) {
        connection.request.content match {
          case Some(content: FormDataContent) => {
            val fileName = content.string("type").value match {
              case "upload" => {
                val fileEntry = content.file("file")
                val ext = extensionFromFileName(fileEntry.fileName)
                val f = File.createTempFile(fileEntry.fileName, s".$ext", directory)
                fileEntry.file.renameTo(f)
                received @= UploadedFile(f, fileEntry.fileName)
                f.getName
              }
              case "mergeSlices" => {
                val fileName = content.string("fileName").value
                val ext = extensionFromFileName(fileName)
                val slices = content.string("slices").value.split(',').toList
                val sources = slices.map(fn => new File(directory, fn))
                val destination = File.createTempFile(fileName, s".$ext", directory)
                IO.merge(sources, destination)
                received @= UploadedFile(destination, fileName)
                destination.getName
              }
            }
            connection.modify { response =>
              response.withContent(Content.string(fileName, ContentType.`text/plain`))
            }
          }
          case _ => connection.modify { response =>
            response.withStatus(HttpStatus.OK).withContent(Content.string("No content sent", ContentType.`text/plain`))
          }
        }
      } else {
        connection.modify { response =>
          response.withStatus(HttpStatus.Forbidden).withContent(Content.string("Authentication failure", ContentType.`text/plain`))
        }
      }
    }
  } else {
    Future.successful(connection)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy