使用Files.copy功能保存数据时,如何提高数据在网络上的传输速度?

时间:2019-05-13 11:52:52

标签: java file sockets kotlin stream

我有一个客户端应用程序,可以将文件发送到服务器应用程序,反之亦然。传输速度确实很慢(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,那么如果可以加快速度,那也将是不受欢迎的。

0 个答案:

没有答案