You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为scala.sys.process的pipe操作添加超时?关联此前异常捕获问题

Adding Timeout to scala.sys.process File Downloads

Hey there, great question! The default scala.sys.process API doesn’t come with built-in timeout handling, which is exactly why you’re hitting that stuck download scenario with large files. Let’s walk through a couple of practical approaches to add that 1-hour timeout and handle cleanup properly if the download hangs.

Approach 1: Use Scala Futures with Await.result

We can wrap the download operation in a Future and use Await.result to enforce our timeout. If the download takes longer than the specified duration, we’ll catch the timeout exception and clean up any partial files.

import java.io.File
import java.net.URL
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.sys.process._
import scala.util.control.NonFatal

def downloadWithTimeout(url: String, outputFile: File, timeout: Duration): Unit = {
  // Wrap the download in a Future to run it asynchronously
  val downloadFuture = Future {
    new URL(url) #> outputFile !
  }

  try {
    val exitCode = Await.result(downloadFuture, timeout)
    println(s"Download finished successfully with exit code: $exitCode")
  } catch {
    case _: java.util.concurrent.TimeoutException =>
      println("Download timed out after the specified duration")
      // Clean up the partial download file if it exists
      if (outputFile.exists()) outputFile.delete()
    case NonFatal(e) =>
      println(s"Download failed with error: ${e.getMessage}")
      if (outputFile.exists()) outputFile.delete()
  }
}

// Example usage: 1-hour timeout
downloadWithTimeout("http://www.scala-lang.org/", new File("scala-lang.html"), 1.hour)

A quick note: The default #> ! syntax doesn’t expose the underlying process, so if a timeout occurs, the download might keep running in the background. For more control, try the next approach.

Approach 2: Explicitly Manage the Process

By using processBuilder.run() instead of !, we get a reference to the running process. We can then use a thread to wait for completion, and terminate the process if the timeout is reached.

import java.io.File
import java.net.URL
import scala.sys.process._
import scala.concurrent.duration._

def downloadWithProcessTimeout(url: String, outputFile: File, timeout: Duration): Unit = {
  val processBuilder = new URL(url) #> outputFile
  // Start the process without blocking
  val process = processBuilder.run()

  // Thread to wait for process completion
  val completionThread = new Thread {
    override def run(): Unit = {
      val exitCode = process.exitValue()
      println(s"Download completed with exit code: $exitCode")
    }
  }
  completionThread.start()

  // Wait for the timeout duration
  completionThread.join(timeout.toMillis)

  if (completionThread.isAlive) {
    // Process is still running - trigger timeout handling
    println("Download timed out, terminating the process...")
    process.destroy()
    // Clean up the partial file
    if (outputFile.exists()) outputFile.delete()
  }
}

// Example usage: 1-hour timeout
downloadWithProcessTimeout("http://www.scala-lang.org/", new File("scala-lang.html"), 1.hour)

This approach is more robust because we can directly terminate the stuck download process, preventing it from lingering in the background.

Key Notes

  • Always clean up partial files after a timeout to avoid cluttering your filesystem.
  • You can extend these methods to add retry logic or more detailed logging if needed.
  • For complex asynchronous workflows, consider using Akka’s schedulers, but the above methods cover most common use cases.

内容的提问来源于stack exchange,提问作者carfield

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:48:12