java - 使用 Files.copy 功能保存数据时,如何提高数据在网络上的传输速度?
问题描述
我有一个客户端应用程序将文件发送到服务器应用程序,反之亦然。传输速度真的很慢(100KB/s),我正在尝试找出问题所在。我的连接速度大约是 100Mbps 下行/12Mbps 上行,并且通过其他应用程序将文件传输到服务器具有更高的吞吐量。
下面是服务器应用程序上的类,它基本上读取数据并将其保存在磁盘上:
class AcceptFileResponseCreator: HttpResponseCreator {
private val response = JSONObject()
override fun computeResponse(httpExchange: HttpExchange): Triple<BufferedInputStream, Int, HashMap<String, String>> {
try {
logger.debug("Start accepting the request of uploading a file to the server...")
val fileSize = httpExchange.requestHeaders.getFirst("Content-length").toLong()
val md5OfTransferredFile = httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_MD5)!!
val targetId = httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_TARGET_ID)!!
val clientId = httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_CLIENT_ID)!!
val clientAuthentication = httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_CLIENT_PASS)!!
val fileName = decodeHTTPHeader(httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_CLIENT_FILENAME)!!)
val fileRelativePath = decodeHTTPHeader(httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_CLIENT_RELATIVE_PATH)!!)
val fileHash = httpExchange.requestHeaders.getFirst(JSONFieldNames.API_HEADER_CLIENT_FILE_HASH)!!
val file = Manager.getEmptyFile()
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}
logger.debug("Start saving the file on the server...")
val amountOfBytesWritten = Files.copy(httpExchange.requestBody, file.toPath())
Manager.addFileAsFileTransfer(fileName, fileRelativePath, fileHash, targetId, clientId, file)
logger.debug("Wrote the file on the server at: ${file.absolutePath}")
if (amountOfBytesWritten == fileSize) {
return if (md5OfTransferredFile == file.md5()) {
logger.debug("File must have been written successfully! ($fileRelativePath/$fileName from client $clientId for $targetId)")
response.put(JSONFieldNames.REQUEST_ACCEPT, true)
Triple(response.toString().toByteArray().inputStream().buffered(), HttpURLConnection.HTTP_OK, HashMap())
} else {
logger.debug("File md5 hash does not match! ($fileRelativePath/$fileName from client $clientId for $targetId)")
constructErrorResponse("File md5 hash uploaded on the server does not match!")
Triple(response.toString().toByteArray().inputStream().buffered(), HttpURLConnection.HTTP_INTERNAL_ERROR, HashMap())
}
}
else {
logger.debug("Amount of bytes written to the disk differ from the amount of the sender's data")
constructErrorResponse("Amount of data received by the server is not correct.")
return Triple(response.toString().toByteArray().inputStream().buffered(), HttpURLConnection.HTTP_INTERNAL_ERROR, HashMap())
}
}
catch (e: Exception) {
Manager.logger.debug("Couldn't parse as JSON", e)
constructErrorResponse("Request could not be parsed as a JSON object.", e.toString()).toString()
return Triple(response.toString().toByteArray().inputStream().buffered(), HttpURLConnection.HTTP_NOT_FOUND, HashMap())
}
}
private fun constructErrorResponse(errorMessage: String = "Generic error", exceptionMessage:String = "") : JSONObject {
response.put(HttpJSONFieldIdentifiers.REQUEST_ACCEPT, false)
response.put(HttpJSONFieldIdentifiers.RESPONSE_ERROR, errorMessage)
return response
}
}
客户端应用程序上发送数据的方法是:
/**
* Sends a request to the server with the content of the data InputStream, at the specified uri. You can specify the request method and add any headers on the request by using the respective parameters.
* @param data The data input stream that will be sent to the server
* @param uri The URI that the request will be sent to
* @param requestMethod The type of the HTTP request
* @param headers The headers of the request
*/
fun sendRequestSynchronous(data: InputStream, uri: String = "/", requestMethod: String = "POST", headers: Map<String, String> = HashMap()) : Triple<Int, MutableMap<String, MutableList<String>>, InputStream> {
val path = "https://$serverHostname:$serverPort$uri"
val connection = if (path.startsWith("https://")) (URL(path).openConnection() as HttpsURLConnection) else URL(path).openConnection() as HttpURLConnection
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier { hostname, _ -> hostname == serverHostname }
if (connection is HttpsURLConnection) {
connection.sslSocketFactory = this.sslFactory
connection.hostnameVerifier = CustomHostnameVerifier(serverHostname)
}
connection.doInput = true
connection.doOutput = true
connection.requestMethod = requestMethod
headers.forEach {
connection.addRequestProperty(it.key, it.value)
}
val totalAmountBytesToSend = data.available() // amount of bytes of the whole request
var bytesSent = 0
var bytesAmountToSent: Int // amount of bytes that will be sent in only one write call
var amountOfBytesReadFromInput: Int
while (bytesSent < totalAmountBytesToSend) {
bytesAmountToSent = totalAmountBytesToSend - bytesSent
if (bytesAmountToSent > ramUsagePerAction)
bytesAmountToSent = ramUsagePerAction
val bytes = ByteArray(bytesAmountToSent)
amountOfBytesReadFromInput = data.read(bytes)
if (amountOfBytesReadFromInput != bytesAmountToSent) {
logger.error("Different amount of bytes read from input stream than expected! Read: $amountOfBytesReadFromInput, expected: $bytesAmountToSent")
}
connection.outputStream.write(bytes)
bytesSent += bytesAmountToSent
}
connection.outputStream.flush()
val code = connection.responseCode
return Triple(code, connection.headerFields, connection.inputStream)
}
有什么我可以改进的吗?如果您的更改不涉及服务器应用程序上的 Files.copy,那么如果它加快了速度,那也是非常受欢迎的。
解决方案
推荐阅读
- python - 健壮的连续 TCP 连接(python 套接字)
- python-3.x - 使用 discord.py 编辑消息
- java - 是否有 Additive/Supplemental ForEach 的编程概念?
- presto - Presto - 获取数组元素的总和
- flutter - 在 ios 中使用 google_maps_flutter-0.5.28+1 应用程序后不起作用
- c# - 无法从 SendGrid 帐户 api 密钥发送电子邮件
- excel - 使用与 15.0 不兼容的 Microsoft Outlook 16.0 对象库的代码?
- angular - 在 Angular 中,我们如何检测 DOM 元素是否具有关联的 ngModel?
- tensorflow - 如何将 resnet_v1_50 检查点加载到 tf.keras ResNet50 模型中?
- javascript - 纯 HTML/JS 如何根据访问者的位置将访问者引导至特定网页