Created
April 14, 2012 22:52
-
-
Save OlegIlyenko/2388377 to your computer and use it in GitHub Desktop.
Simple file downloader (http://hacking-scala.posterous.com/progress-monitoring-for-streams)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package scaldi.keys | |
import math._ | |
import swing._ | |
import java.io.{Closeable, InputStream, FilterInputStream} | |
import javax.swing.UIManager | |
import java.net.URL | |
object Downloader extends SimpleSwingApplication { | |
UIManager.getInstalledLookAndFeels | |
.filter(_.getName contains "Nimbus").headOption | |
.map(_.getClassName).foreach(UIManager setLookAndFeel _) | |
val url = new TextField(35) | |
val progress = new ProgressBar | |
val info = new Label("Please start download") | |
def top = new MainFrame { | |
title = "File Downloader" | |
contents = new BoxPanel(Orientation.Vertical) { | |
contents += new FlowPanel(FlowPanel.Alignment.Right)( | |
new Label("URL:"), | |
url, | |
Button("Download") { | |
import Util._ | |
thread { | |
val conn = new URL(url.text).openConnection | |
val tracker = (p: Progress) => Swing.onEDT { | |
info.text = "<html>" + p.formattedMetrics.mkString("<br>") +"</html>" | |
progress.value = p.percent | |
} | |
withResource(ProgressInputStream(conn.getInputStream, conn.getContentLength, tracker)) { in => | |
val buf = new Array[Byte](1024) | |
while(in.read(buf) != -1) { | |
println("Reading 1 kb") | |
} | |
} | |
} | |
} | |
) | |
contents += progress | |
contents += new FlowPanel(FlowPanel.Alignment.Left)(info) | |
} | |
size = new Dimension(550, 250) | |
centerOnScreen() | |
} | |
} | |
object Util { | |
def thread(fn: => Unit) = { | |
val thread = new Thread(Swing.Runnable(fn)) | |
thread.start() | |
thread | |
} | |
def withResource[T <: Closeable, R](res: T)(fn: T => R) = | |
try { | |
fn(res) | |
} finally { | |
try { | |
res.close() | |
} catch { | |
case e: Exception => e.printStackTrace() | |
} | |
} | |
} | |
class ProgressInputStream(in: InputStream, listener: Long => Unit) extends FilterInputStream(in) { | |
val NotificationThreshold = 8 * 1024; | |
var unnotifiedByteCount = 0 | |
override def read() = { | |
val data = super.read() | |
if (data != -1) notify(1) | |
data | |
} | |
override def read(b: Array[Byte], off: Int, len: Int) = { | |
val bytesRead = super.read(b, off, len) | |
if (bytesRead != -1) notify(bytesRead) | |
bytesRead | |
} | |
override def close() { | |
if (unnotifiedByteCount > 0) { | |
listener(unnotifiedByteCount) | |
unnotifiedByteCount = 0 | |
} | |
super.close() | |
} | |
def notify(bytesRead: Int) { | |
unnotifiedByteCount += bytesRead | |
if (unnotifiedByteCount >= NotificationThreshold) { | |
listener(unnotifiedByteCount) | |
unnotifiedByteCount = 0 | |
} | |
} | |
} | |
object ProgressInputStream { | |
def apply(in: InputStream, availableBytes: Long, tracker: Progress => Unit) = | |
new ProgressInputStream(in, new ProgressListener(availableBytes, tracker)) | |
} | |
case class Progress(percent: Int, size: Long, remains: Long, done: Long, bps: Long, elapsed: Long, estimated: Long) { | |
lazy val formattedMetrics= List( | |
"Progress: " + percent + "%", | |
"File size: " + formatSize(size), | |
"Downloaded: " + formatSize(done), | |
"Remains: " + formatSize(remains), | |
"Speed: " + (bps / 1024) + " kb/s", | |
"Elapsed: " + formatTime(elapsed), | |
"Estimated: " + formatTime(estimated) | |
) | |
def formatTime(time: Long) = { | |
val oneSecond = 1000 | |
val oneMinute = 60 * oneSecond | |
val oneHour = 60 * oneMinute | |
val hours = (time / oneHour).toInt | |
val minutes = ((time - hours * oneHour) / oneMinute).toInt | |
val seconds = ((time - hours * oneHour - minutes * oneMinute) / oneSecond).toInt | |
val fmt = "%02d" | |
List(hours, minutes, seconds) map (fmt format _) mkString ":" | |
} | |
def formatSize(bytes: Long) = | |
if (bytes < 1024) | |
bytes + " B" | |
else { | |
val exp = (log(bytes) / log(1024)).toInt | |
"%.1f %sB" format (bytes / pow(1024, exp), "KMGTPE" charAt (exp - 1)) | |
} | |
} | |
class ProgressListener(availableBytes: Long, tracker: Progress => Unit) extends Function1[Long, Unit] { | |
var transfered: Long = 0 | |
val startTime = System.currentTimeMillis | |
def apply(bytesTransfered: Long) { | |
transfered += bytesTransfered | |
val elapsed = System.currentTimeMillis - startTime | |
val bpms = (transfered / max(elapsed, 1000)).toLong | |
val bps = (transfered / max(elapsed / 1000, 1)).toLong | |
tracker(Progress( | |
percent = (transfered * 100 / availableBytes).toInt min 100, | |
size = availableBytes, | |
remains = availableBytes - transfered, | |
done = transfered, | |
bps = bps, | |
elapsed = elapsed, | |
estimated = (availableBytes - transfered) / max(bpms, 1) | |
)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment