Hi everybody !
Following code is a server realized with akka-http on android that serves files.
For this to work, just call new ServerForDownloadFile() in an android AsyncTask.
class ServerForDownloadTask extends AsyncTask[AnyRef, Void, AnyRef] {
protected def doInBackground(args: AnyRef*): AnyRef = {
try {
new ServerForDownloadFile()
} catch { case e: Exception => println(e) }
}
return null
}
protected def onProgressUpdate(progress: Integer*) {}
protected def onPostExecute(result: Long) {}
}
Take care : this will work only with scala 2.11 (not with scala 2.12) because android is java 6 compatible (not java 8).
ServerForDownloadFile.scala
======================
package app
import java.io.File
import java.io.FileInputStream
import java.nio.channels.FileChannel
import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Try
import scala.util.control.NonFatal
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart
import akka.http.scaladsl.model._
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import akka.stream.scaladsl.Source
import akka.util.ByteString
import akka.util.Timeout
import android.os.Environment
class ByteBufferIterator(buffer:ByteBuffer, chunkSize:Int) extends Iterator[ByteString] {
require(buffer.isReadOnly)
require(chunkSize > 0)
override def hasNext = buffer.hasRemaining
override def next(): ByteString = {
val size = chunkSize min buffer.remaining()
val temp = buffer.slice()
temp.limit(size)
buffer.position(buffer.position() + size)
ByteString(temp)
}
}
class ServerForDownloadFile {
def map1(path: String) : MappedByteBuffer = {
val inputStream = new FileInputStream(path)
val channel: FileChannel = inputStream.getChannel();
val result = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size);
channel.close()
result
}
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val askTimeout: Timeout = 500.millis
import HttpMethods._
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, uri, headers, _, _) =>
val dir: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val path: String = dir.getPath.toString + "/myfile.avi"
println("=========== path=" + path)
val result = Try {
val mappedByteBuffer = map1(path)
val iterator = new ByteBufferIterator(mappedByteBuffer, 4096)
var cnt = 0
val chunks = Source(() => iterator).map { x =>
if ( cnt % 10000 == 0 )
println("Chunk of size " + x.size + " cnt=" + cnt)
cnt += 1
ChunkStreamPart(x)
}
HttpResponse(entity = HttpEntity.Chunked(MediaTypes.`application/octet-stream`, chunks))
} recover {
case NonFatal(cause) =>
HttpResponse(StatusCodes.InternalServerError, entity = cause.getMessage)
}
result.get
case _: HttpRequest => HttpResponse(StatusCodes.NotFound, entity = "Unknown resource!")
}
val address = "localhost"
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] = Http(system).bind(interface = address, port = 8080)
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
// foreach materializes the source
println("Accepted new connection from " + connection.remoteAddress)
// ... and then actually handle the connection
connection.handleWithSyncHandler(requestHandler)
}).run()
while (true )
Thread.sleep(100)
}