in ideaSupport/src/main/scala/org/jetbrains/sbtidea/download/FileDownloader.scala [185:262]
private def downloadNative(
url: URL,
reuseExistingPartFile: Boolean,
isFirstAttempt: Boolean
)(progressCallback: ProgressCallback): DownloadedPath = {
val connection = url.openConnection()
connection.setConnectTimeout(SocketConnectionTimeoutMs)
connection.setReadTimeout(SocketReadTimeoutMs)
val remoteMetaData = fetchRemoteMetaData(url)
val fileNameWithoutPartSuffix =
if (remoteMetaData.fileName.nonEmpty)
remoteMetaData.fileName
else
Math.abs(url.hashCode()).toString
val to = downloadDirectory.resolve(fileNameWithoutPartSuffix)
val toLength = if (to.toFile.exists()) Files.size(to) else -1
//The file can be present if this VM option was set in previous project import run:
// `-Dsbt.idea.plugin.keep.downloaded.files`
//
//NOTE: file can be present but can have a different size for some idea versions
//E.g. ideaIU-231.7515-EAP-CANDIDATE-SNAPSHOT can in reality represent different 231.7517.* versions
//Because when new EAP-CANDIDATE is uploaded, the previous one is overridden
if (remoteMetaData.length == toLength) {
log.warn(s"File already downloaded: $to")
return DownloadedPath.ZipPath(to)
}
val toPart = toFilePartPath(downloadDirectory.resolve(fileNameWithoutPartSuffix))
val tpPartLength = if (toPart.toFile.exists()) Files.size(toPart) else -1
if (remoteMetaData.length == tpPartLength) {
log.warn(s"Part file already downloaded: recovering interrupted install...")
return DownloadedPath.PartPath(toPart)
}
val resumePartFileDownload = toPart.toFile.exists() &&
isResumeSupported(url) &&
// reuseExistingPartFile is effective only during the first download try
(reuseExistingPartFile || !isFirstAttempt)
if (resumePartFileDownload) {
connection.setRequestProperty("Range", s"bytes=$tpPartLength-")
log.info(s"Resuming download of $url to $toPart")
} else {
Files.deleteIfExists(toPart)
log.info(s"Starting download $url to $toPart")
}
var inChannel: ReadableByteChannel = null
var outStream: FileOutputStream = null
try {
val blockSize = 1 << 24 // 16M
var transferred = -1L
inChannel = Channels.newChannel(connection.getInputStream)
outStream = new FileOutputStream(toPart.toFile, toPart.toFile.exists())
val expectedFileSize = remoteMetaData.length
val rbc = new RBCWrapper(inChannel, expectedFileSize, tpPartLength, progressCallback, toPart)
val fileChannel = outStream.getChannel
var position = fileChannel.position()
while (transferred != 0) {
transferred = fileChannel.transferFrom(rbc, position, blockSize)
position += transferred
}
if (expectedFileSize > 0 && Files.size(toPart) != expectedFileSize) {
throw new DownloadException(s"Incorrect downloaded file size: expected $expectedFileSize, got ${Files.size(toPart)}")
} else {
rbc.ensureFinalLogProgressCall()
}
DownloadedPath.PartPath(toPart)
} finally {
try { if (inChannel != null) inChannel.close() } catch { case e: Exception => log.error(s"Failed to close input channel: $e") }
try { if (outStream != null) outStream.close() } catch { case e: Exception => log.error(s"Failed to close output stream: $e") }
}
}