Permalink
Browse files

multi file upload implementiert

  • Loading branch information...
1 parent cabf2cd commit 0c858aefb45d347b8251b13f3bac4c2fc6c84383 @KyleRogers committed Oct 22, 2013
@@ -4,9 +4,18 @@ import play.api.mvc._
import jp.t2v.lab.play2.auth.AuthenticationElement
import model._
import com.google.inject._
-import model.BenutzerName
-import scala.Some
import play.api.libs.iteratee.Enumerator
+import play.api.Logger
+import scala.Some
+import model.Gueltig
+import play.api.mvc.SimpleResult
+import model.BenutzerName
+import model.BilderMitFehlern
+import play.api.mvc.ResponseHeader
+import model.AlleBilderGueltig
+import scala.concurrent.Await
+import scala.concurrent.duration.Duration
+import java.util.concurrent.TimeUnit
/**
*
@@ -19,6 +28,8 @@ class FotoVorfuehrer @Inject()(
verwalter: FotoalbenVerwalter
) extends Controller with AuthenticationElement with WeddingAuthConfig {
+ private val log = Logger("hochladen")
+
def fotoalben = StackAction{ implicit request =>
Ok(views.html.fotoalben(verwalter.alleFotoalben()))
}
@@ -64,18 +75,59 @@ class FotoVorfuehrer @Inject()(
def hochladen = StackAction(parse.multipartFormData){ implicit request =>
val currentUser = loggedIn
- request.body.file("bilddatei").map { picture =>
- fotoImporter.importiere(picture.ref.file, currentUser)
+ log.info(s"Hochladen gestartet durch $currentUser")
+
+ Async {
+ scala.concurrent.Future {
+
+ val pruefergebnisse = request.body.files.map { filePart =>
+ BildDateiPruefer.pruefeBild(filePart)
+ }
+
+ val ungueltige = pruefergebnisse collect { case g:Fehler => g }
- Redirect(routes.FotoVorfuehrer.fotoalben()).flashing(
- "erfolgsMeldung" -> "Bild erfolgreich zu deinem Album hinzugefügt."
- )
- }.getOrElse {
- Redirect(routes.FotoVorfuehrer.fotoalben()).flashing(
- "fehlerMeldung" -> "Keine Datei ausgewählt."
- )
+ if(!ungueltige.isEmpty) {
+ BilderMitFehlern(ungueltige)
+ } else {
+ AlleBilderGueltig(pruefergebnisse collect {
+ case g: Gueltig => g
+ })
+ }
+ }.map {
+ case AlleBilderGueltig(gueltige) => {
+ log.info("start import map")
+ val importTasks = gueltige.map { g =>
+ val p = g.filePart
+ log.info("mapping to import jobs")
+ scala.concurrent.Future{
+ log.info(s"import file <${p.filename}>")
+ fotoImporter.importiere(p.ref.file, currentUser)
+ log.info(s"import file <${p.filename}> complete")
+ p
+ }
+ }
+
+ Left(scala.concurrent.Future.fold(importTasks)(0){ ( sum, filePart) =>
+ log.info(s"fold file import <${filePart.filename}>")
+ sum + 1
+ })
+ }
+ case BilderMitFehlern(ungueltige) => Right(BilderMitFehlern(ungueltige))
+ }.map {
+ case Left(promise) => {
+ Await.result(promise, Duration(10, TimeUnit.MINUTES)) match {
+ case 0 => Redirect(routes.FotoVorfuehrer.fotoalben()).flashing(
+ "fehlerMeldung" -> "Es wurde keine Datei importiert. Keine Datei ausgewählt?")
+ case sum => Redirect(routes.FotoVorfuehrer.fotoalben()).flashing(
+ "erfolgsMeldung" -> s"$sum Bild(er) erfolgreich zu deinem Album hinzugefügt.")
+ }
+ }
+ case Right(BilderMitFehlern(ungueltige)) => {
+ Redirect(routes.FotoVorfuehrer.fotoalben()).flashing(
+ "fehlerMeldung" -> "Mindestens eine Datei konnte nicht importiert werden, weil sie kein Bild oder zu groß war (max. 10 MB).")
+ }
+ }
}
}
-
}
@@ -0,0 +1,87 @@
+package model
+
+import play.api.mvc.MultipartFormData
+import play.api.libs.Files
+import net.sf.jmimemagic.{MagicMatch, MagicMatchNotFoundException, Magic}
+import play.api.Logger
+import play.api.mvc.MultipartFormData.FilePart
+import play.api.libs.Files.TemporaryFile
+import java.io.File
+
+/**
+ *
+ * @author Stefan Penndorf <stefan@cyphoria.net>
+ */
+sealed abstract class BildDateiPruefergebnis(val filename: String)
+case class Gueltig(filePart: MultipartFormData.FilePart[Files.TemporaryFile]) extends BildDateiPruefergebnis(filePart.filename)
+sealed abstract class Fehler(filename: String) extends BildDateiPruefergebnis(filename)
+case class ZuGross(filePart: MultipartFormData.FilePart[Files.TemporaryFile]) extends Fehler(filePart.filename)
+case class KeineBilddatei(filePart: MultipartFormData.FilePart[Files.TemporaryFile]) extends Fehler(filePart.filename)
+
+sealed abstract class BilderBatchPruefergebnis()
+case class AlleBilderGueltig(bilder: Seq[Gueltig])
+case class BilderMitFehlern(fehler: Seq[Fehler])
+
+
+object BildDateiPruefer {
+
+ private val log = Logger("hochladen")
+
+ private val MAXIMALE_DATEIGROESSE: Int = 10 * 1024 * 1024
+
+ type Prueffunktion = FilePart[TemporaryFile] => BildDateiPruefergebnis
+
+ def dateiGroessenPruefer: Prueffunktion = (filePart: FilePart[TemporaryFile]) => {
+ if (filePart.ref.file.length() > MAXIMALE_DATEIGROESSE) {
+ log.info(s"Datei ist zu groß <${filePart.filename}>")
+ ZuGross(filePart)
+ } else {
+ Gueltig(filePart)
+ }
+ }
+
+ def dateiErweiterungPruefer: Prueffunktion = (filePart: FilePart[TemporaryFile]) => {
+ if (!filePart.filename.toLowerCase.endsWith(".png") && !filePart.filename.toLowerCase.endsWith(".jpg")) {
+ log.info(s"Datei hat falsche Endung <${filePart.filename}>")
+ KeineBilddatei(filePart)
+ } else {
+ Gueltig(filePart)
+ }
+ }
+
+ def dateiInhaltPruefer: Prueffunktion = (filePart: FilePart[TemporaryFile]) => {
+ tryMagicMatch(filePart.ref.file).map( result =>
+ if(!result.getMimeType.startsWith("image")) {
+ log.info(s"Datei hat falschen Typ <${filePart.filename}> <${Magic.getMagicMatch(filePart.ref.file, true).getMimeType}>")
+ KeineBilddatei(filePart)
+ } else {
+ Gueltig(filePart)
+ }
+ ).getOrElse(
+ KeineBilddatei(filePart)
+ )
+ }
+
+ private def tryMagicMatch(file: File): Option[MagicMatch] = {
+ try {
+ Some(Magic.getMagicMatch(file, true))
+ } catch {
+ case e: MagicMatchNotFoundException => None
+ }
+ }
+
+
+ def pruefeBild(filePart: MultipartFormData.FilePart[Files.TemporaryFile]): BildDateiPruefergebnis = {
+ log.info(s"Prüfe Datei <${filePart.filename}>")
+
+ Seq(dateiGroessenPruefer, dateiErweiterungPruefer, dateiInhaltPruefer).foldLeft(Gueltig(filePart).asInstanceOf[BildDateiPruefergebnis])(fuehrePruefungAus)
+ }
+
+ def fuehrePruefungAus: (BildDateiPruefergebnis, Prueffunktion) => BildDateiPruefergebnis = {
+ (res, pruefer) => res match {
+ case Gueltig(filePart) => pruefer(filePart)
+ case _ => res
+ }
+ }
+}
+
@@ -30,7 +30,7 @@
<div class="clearfix">
<label for="bilddatei">Bild ausw&auml;hlen:</label>
<div class="input">
- <input type="file" size="50" name="bilddatei" id="bildDatei" accept="image" multiple="multiple" />
+ <input type="file" size="50" name="bilddatei" id="bildDatei" accept="image/png,image/jpeg" multiple="multiple" />
</div>
</div>
</fieldset>
View
@@ -58,5 +58,7 @@ logger.play=INFO
# Logger provided to your application:
logger.application=DEBUG
+logger.hochladen=INFO
+
smtp.mock=true
@@ -43,7 +43,7 @@ class FotoalbenSeite extends FluentPage with ShouldMatchers with ImageCompareMat
await().atMost(3, TimeUnit.SECONDS).until(new Predicate[WebDriver] {
def apply(p1: WebDriver): Boolean = {
val messages: String = $(".alert-message.success").getText
- messages != null && messages.contains("Bild erfolgreich zu deinem Album hinzugefügt.")
+ messages != null && messages.contains("Bild(er) erfolgreich zu deinem Album hinzugefügt.")
}
})

0 comments on commit 0c858ae

Please sign in to comment.