在scala Play框架中,在操作结果中流式传输ByteArrayOutputStream

问题描述 投票:1回答:1

我正在尝试在scala播放框架中将svg图像转换为jpeg图像。

我使用了蜡染,它工作正常。

现在,我喜欢在操作结果中流式传输输出,而不是将ByteArrayOutputStream转换为ByteArray将整个输出加载到内存中。

我该怎么做?

这里是项目代码,无需流输出即可工作:

build.sbt

name := "svg2png"

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

resolvers += Resolver.sonatypeRepo("snapshots")

scalaVersion := "2.12.3"

libraryDependencies ++= Seq(
  jdbc,
  guice,
  "org.apache.xmlgraphics" % "batik-transcoder" % "1.11",
  "org.apache.xmlgraphics" % "batik-codec" % "1.11"
)

/ project / plugins.sbt

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.6")

/ project / build.properties

sbt.version=1.0.2

/ conf / routes

GET     /                                          controllers.Application.index
GET     /image                                     controllers.Application.getImage

/ conf / application.conf

play.filters.enabled += "play.filters.cors.CORSFilter"

play.filters {
  hosts {
    allowed = ["."]
  }
  headers {
    contentSecurityPolicy = null
  }
}

play.i18n {
  langs = [ "en" ]
}

contexts {
  imageService {
    fork-join-executor {
      parallelism-factor = 4.0
      parallelism-max = 8
    }
  }
}

/ app / controllers / Application.scala

package controllers

import play.api.mvc._
import javax.inject._
import scala.concurrent.ExecutionContext

@Singleton
class Application @Inject()(imageService: services.ImageService,
                            cc: ControllerComponents)(implicit exec: ExecutionContext) extends AbstractController(cc) {

  def index = Action {
    Ok("test app").as(HTML)
  }

  def getImage : Action[AnyContent] =  Action.async {
    imageService.getImage.map{res => Ok(res).as("image/jpeg")  }
  }

}

/ app / services / ImageService.scala

package services

import java.io.{ByteArrayOutputStream, StringReader}

import com.google.inject.{Inject, Singleton}

import scala.concurrent.{ExecutionContext, Future}
import akka.actor.ActorSystem
import org.apache.batik.transcoder.image.JPEGTranscoder
import org.apache.batik.transcoder.TranscoderInput
import org.apache.batik.transcoder.TranscoderOutput

@Singleton
class ImageService @Inject()(actorSystem: ActorSystem) {

  implicit val AnalyticsServiceExecutionContext: ExecutionContext = actorSystem.dispatchers.lookup("contexts.imageService")

  def getImage: Future[Array[Byte]] = {
    Future {
      val t: JPEGTranscoder = new JPEGTranscoder
      t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new java.lang.Float(0.8))

      val imageSVGString: String =
        """<svg width="1000" height="1000" viewBox="0 0 1000 1000" version="1.1"
          |     xmlns="http://www.w3.org/2000/svg"
          |     xmlns:xlink="http://www.w3.org/1999/xlink">
          |  <circle cx="500" cy="500" r="300" fill="lightblue" />
          |</svg>
        """.stripMargin
      val input: TranscoderInput = new TranscoderInput(new StringReader(imageSVGString))

      val outputStream = new ByteArrayOutputStream
      val output: TranscoderOutput = new TranscoderOutput(outputStream)

      t.transcode(input, output)
      outputStream.toByteArray
    }
  }
}
scala playframework akka-stream
1个回答
0
投票

这对我有用:

svg match {
  case None => NotFound
  case Some(svg) =>
    val svgImage = new TranscoderInput(new StringReader(svg))
    val pngOstream = new ByteArrayOutputStream
    val outputPngImage = new TranscoderOutput(pngOstream)
    val converter = fileExtension match {
      case "png" => new PNGTranscoder()
      case _ => new JPEGTranscoder()
    }
    if(converter.isInstanceOf[JPEGTranscoder]){
      converter.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, (0.8).toFloat)
    }
    converter.transcode(svgImage, outputPngImage)
    Ok(pngOstream.toByteArray).as("image/" + fileExtension)
}
© www.soinside.com 2019 - 2024. All rights reserved.