ResourceBox.scala
package wechaty.puppet
import java.io._
import java.net.{HttpURLConnection, URL}
import java.util.Base64
import javax.activation.MimeType
import org.apache.commons.io.IOUtils
import wechaty.puppet.ResourceBox.ResourceBoxType
import wechaty.puppet.ResourceBox.ResourceBoxType.Type
import wechaty.puppet.schemas.Puppet.objectMapper
/**
* wrap stream,support file stream and url stream
* @author <a href="mailto:jcai@ganshane.com">Jun Tsai</a>
* @since 2020-06-08
*/
object ResourceBox {
def fromFile(file: File): ResourceBox={
new FileResourceBox(file)
}
def fromUrl(url: String): ResourceBox={
new UrlResourceBox(url)
}
def fromBase64(name:String,base64: String): ResourceBox={
new Base64ResourceBox(name,base64)
}
def fromJson(json: String): ResourceBox={
val root = objectMapper.readTree(json)
val boxTypeInt = root.get("boxType").asInt()
val boxType = ResourceBoxType.apply(boxTypeInt)
boxType match{
case ResourceBoxType.Base64 =>
fromBase64(root.get("name").asText,root.get("base64").asText)
case ResourceBoxType.Url =>
fromUrl(root.get("remoteUrl").asText)
case _ =>
throw new UnsupportedOperationException("boxType %s unsupported".format(boxType))
}
}
object ResourceBoxType extends Enumeration {
type Type = Value
val Unknown: Type = Value(0)
/**
* Serializable by toJSON()
*/
val Base64 :Type = Value(1)
val Url :Type = Value(2)
val QRCode :Type = Value(3)
/**
* Not serializable by toJSON()
* Need to convert to FileBoxType.Base64 before call toJSON()
*/
val Buffer :Type = Value(4)
val File :Type = Value(5)
val Stream :Type = Value(6)
}
private class UrlResourceBox (val url:String) extends AbstractResourceBox{
override def toStream: InputStream = {
val connection = new URL(url).openConnection.asInstanceOf[HttpURLConnection]
connection.setConnectTimeout(5000)
connection.setReadTimeout(5000)
connection.setRequestMethod("GET")
connection.setRequestProperty("User-Agent", "wechaty/scala")
connection.getInputStream
}
override def toJson(): String = {
val objectNode = objectMapper.createObjectNode
objectNode.put("name",name)
objectNode.put("boxType",ResourceBoxType.Url.id)
objectNode.put("remoteUrl",url)
objectNode.toString
}
override def name: String = url.substring(url.lastIndexOf("/")+1)
override def resourceType: Type = ResourceBoxType.Url
}
private class Base64ResourceBox (override val name:String,base64:String) extends AbstractResourceBox{
override def toStream: InputStream = {
//decode base64 as byte array input stream
new ByteArrayInputStream(Base64.getDecoder.decode(base64))
}
override def toBase64: String = base64
override def toJson(): String = {
val objectNode = objectMapper.createObjectNode
objectNode.put("name",name)
objectNode.put("boxType",ResourceBoxType.Base64.id)
objectNode.put("base64",base64)
objectNode.toString
}
override def resourceType: Type = ResourceBoxType.Base64
}
private class StreamResourceBox (override val name:String,stream:InputStream) extends AbstractResourceBox{
override def toStream: InputStream = stream
override protected def using[T <: Closeable, R](resource: T)(block: T => R): R = {
block(resource) //don't close the stream.must be closed by creator
}
override def resourceType: Type = ResourceBoxType.Stream
}
private class FileResourceBox(file:File) extends AbstractResourceBox{
override def toStream: InputStream = new FileInputStream(file)
override def name: String = file.getName
override def resourceType: Type = ResourceBoxType.File
}
private trait AbstractResourceBox extends ResourceBox {
override def toBase64: String = {
using(toStream) { fi =>
val byteArrayOutputStream = new ByteArrayOutputStream()
val base64Out = Base64.getEncoder.wrap(byteArrayOutputStream)
IOUtils.copy(fi, base64Out)
base64Out.close()
byteArrayOutputStream.close()
byteArrayOutputStream.toString()
}
}
override def toDataURL(mimeType: MimeType): String = {
Array("data:", mimeType, ";base64,", toBase64).mkString("")
}
protected def using[T <: Closeable, R](resource: T)(block: T => R): R = {
try {
block(resource)
} finally {
if (resource != null) resource.close()
}
}
}
}
trait ResourceBox {
def resourceType:ResourceBoxType.Type
def name:String
def toStream:InputStream
def toBase64:String
def toDataURL(mimeType: MimeType):String
def toJson():String = ???
}