Soulever le téléchargement des images, redimensionner, stocker dans la base de données, afficher
-
05-07-2019 - |
Question
Existe-t-il un exemple succinct de la façon de télécharger une image, de la redimensionner, de la stocker dans une base de données, puis de diffuser l'image avec Lift?
Je suis sûr que je pourrais le rassembler à partir des API de téléchargement de fichier, d'API Java 2D, de mappeur d'élévation et de réponse. Mais y a-t-il un exemple de code que je peux suivre pour le faire de manière "correcte" ou recommandée?
La solution
Je l'ai fait pour un champ Mapper lié à s3 en créant un nouveau MappedField. J'ai aussi un code à redimensionner, mais je n'ai pas encore testé ni déployé (utilisez-le avec prudence).
class MappedS3Image[T<:Mapper[T]](owner: T, val path:String, maxWidth: String, maxHeight:String) extends MappedString[T](owner, 36) {
def url:String = MappedS3Image.fullImgPath(path, is)
def setFromUpload(fileHolder: Box[FileParamHolder]) = {
S3Sender.uploadImageToS3(path, fileHolder).map(this.set(_))
}
override def asHtml:Node = <img src={url} style={"max-width:" + maxWidth + ";max-height:"+maxHeight} />
override def _toForm: Box[Elem] = Full(SHtml.fileUpload(fu=>setFromUpload(Full(fu))))
}
import java.awt.Image
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.awt.Graphics2D
import java.awt.AlphaComposite
object ImageResizer {
def resize(is:java.io.InputStream, maxWidth:Int, maxHeight:Int):BufferedImage = {
val originalImage:BufferedImage = ImageIO.read(is)
val height = originalImage.getHeight
val width = originalImage.getWidth
if (width <= maxWidth && height <= maxHeight)
originalImage
else {
var scaledWidth:Int = width
var scaledHeight:Int = height
val ratio:Double = width/height
if (scaledWidth > maxWidth){
scaledWidth = maxWidth
scaledHeight = (scaledWidth.doubleValue/ratio).intValue
}
if (scaledHeight > maxHeight){
scaledHeight = maxHeight
scaledWidth = (scaledHeight.doubleValue*ratio).intValue
}
val scaledBI = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB)
val g = scaledBI.createGraphics
g.setComposite(AlphaComposite.Src)
g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose
scaledBI
}
}
}
Autres conseils
L’autre réponse explique bien comment redimensionner l’image et stocker une référence au fichier dans le système de fichiers.
Si vous souhaitez utiliser le mappeur d'ascenseurs pour stocker le contenu réel du fichier, vous devez créer votre objet modèle personnalisé et définir un champ binaire sur celui-ci. Essayez quelque chose comme ça:
package code {
package model {
import _root_.net.liftweb.mapper._
import _root_.net.liftweb.util._
import _root_.net.liftweb.common._
// singleton object which manipulates storing of Document instances
object Document extends Document with KeyedMetaMapper[Long, Document] {
}
class Document extends KeyedMapper[Long, Document] {
def getSingleton = Document
def primaryKeyField = id
object id extends MappedLongIndex(this)
object name extends MappedString(this, 20) {
override def displayName = "Name"
override def writePermission_? = true
}
object content extends MappedBinary(this) {
override def displayName = "Content"
override def writePermission_? = true
}
}
}
}
Ensuite, dans la classe bootstrap, ajoutez ce Document
à la fin:
Schemifier.schemify(true, Schemifier.infoF _, User, Document)
Voila. Utiliser Document save (nouveau document)
le stocke dans la base de données. Les champs d'un nouveau document
peuvent être définis à l'aide de la méthode set
. Essayez de jouer avec delete _!
, trouver
, méthodes findAll
du singleton Document
à supprimer ou à rechercher dans la base de données. . Cela devrait être simple à partir de maintenant.
Enfin, pour afficher l'image, vous pouvez remplacer les règles de répartition de Lift (dans la classe bootstrap, Boot.scala). Essayez de vous amuser avec cet exemple, qui remplace les règles applicables aux requêtes pdf:
def getFile(filename: String): Option[Document] = {
val alldocs = Document.findAll()
alldocs.find(_.name.get == filename)
}
LiftRules.statelessDispatchTable.append {
case Req("file" :: name :: Nil, "pdf", GetRequest) =>
() =>
println("Got request for: " + name + ".pdf")
for {
stream <- tryo(
getFile(name + ".pdf") map {
doc => new java.io.ByteArrayInputStream(doc.content.get)
} getOrElse null
)
if null ne stream
} yield StreamingResponse(stream,
() => stream.close,
stream.available,
List("Content-Type" -> "application/pdf"),
Nil,
200)
}
Sur la base de la réponse acceptée par Jon Hoffman, j’ai corrigé les bugs. Sa version gâche le rapport d'aspect (il devient toujours 1: 1), parce que le calcul était erroné à quelques endroits près. Cette version redimensionne les grandes images jusqu’à leur adaptation et respecte le rapport de format.
def resize(is:java.io.InputStream, maxWidth:Int, maxHeight:Int):BufferedImage = {
require (maxWidth > 0)
require (maxHeight > 0)
val originalImage:BufferedImage = ImageIO.read(is)
var height = originalImage.getHeight
var width = originalImage.getWidth
// Shortcut to save a pointless reprocessing in case the image is small enough already
if (width <= maxWidth && height <= maxHeight)
originalImage
else {
// If the picture was too big, it will either fit by width or height.
// This essentially resizes the dimensions twice, until it fits
if (width > maxWidth){
height = (height.doubleValue() * (maxWidth.doubleValue() / width.doubleValue())).intValue
width = maxWidth
}
if (height > maxHeight){
width = (width.doubleValue() * (maxHeight.doubleValue() / height.doubleValue())).intValue
height = maxHeight
}
val scaledBI = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
val g = scaledBI.createGraphics
g.setComposite(AlphaComposite.Src)
g.drawImage(originalImage, 0, 0, width, height, null);
g.dispose
scaledBI
}
}