Why I can not upload first chunk of file by using HttpUrlConnection?

徘徊边缘 提交于 2020-02-24 12:57:48

问题


In my project, I should download a file chunk by chunk from one server and upload each chunk immediately to another server.

I have a URL of the file from where I should download. Let's call it downloadUrl.

So, this is how I download file chunk by chunk:

val chunkSize = 1024 * 1024

BufferedInputStream(downloadUrl.openStream()).use { bis ->
    val buffer = ByteArray(chunkSize)
    var countBytesRead: Int

    while (bis.read(buffer, 0, chunkSize).also { countBytesRead = it } != -1) {

        // I should send buffer to another server
    }
}

Now, I should send each chunk to another server. Code I do:

val chunkSize = 1024 * 1024

BufferedInputStream(downloadUrl.openStream()).use { bis ->
    val buffer = ByteArray(chunkSize)
    var countBytesRead: Int

    val boundary = System.currentTimeMillis().toString(16)
    val chunks = fileContentLength / chunkSize + if (contentLength % chunkSize != 0L) 1 else 0

    while (bis.read(buffer, 0, chunkSize).also { countBytesRead = it } != -1) {
        uploadChunkOfFile(
            boundary = boundary,
            chunk = buffer,
            fileFullName = fileFullName,
            chunkId = chunkId,
            chunks = chunks.toInt()
        )
        chunkId++
    }
}

fun uploadChunkOfFile(boundary: String, chunk: ByteArray, fileFullName: String, chunkId: Int, chunks: Int): String {
    val url = "some_upload_url"
    val connection = url.openConnection() as HttpURLConnection
    val lineEnd = "\r\n"
    val twoHyphens = "--"

    val charset = "UTF-8"

    connection.readTimeout = 20000
    connection.connectTimeout = 20000

    connection.doInput = true
    connection.doOutput = true
    connection.useCaches = false
    connection.requestMethod = "POST"
    connection.setRequestProperty("Connection", "Keep-Alive")

    connection.addRequestProperty("token", "some_token")
    connection.addRequestProperty("Content-Type", "multipart/form-data; boundary=$boundary")

    val dos = DataOutputStream(connection.outputStream)

    dos.writeBytes("$boundary$lineEnd")

    // Send parameter #chunkId
    dos.writeBytes("Content-Disposition: form-data; name=\"chunk\"$lineEnd")
    dos.writeBytes("Content-Type: text/plain; charset=$charset$lineEnd")
    dos.writeBytes("Content-Length: " + chunkId.toString().length + lineEnd)
    dos.writeBytes(lineEnd)
    dos.writeBytes(chunkId.toString() + lineEnd)
    dos.writeBytes(twoHyphens + boundary + lineEnd)

    // Send parameter #chunks
    dos.writeBytes("Content-Disposition: form-data; name=\"chunks\"$lineEnd")
    dos.writeBytes("Content-Type: text/plain; charset=$charset$lineEnd")
    dos.writeBytes("Content-Length: " + chunks.toString().length + lineEnd)
    dos.writeBytes(lineEnd)
    dos.writeBytes(chunks.toString() + lineEnd)
    dos.writeBytes(twoHyphens + boundary + lineEnd)

    // Send parameter #name
    dos.writeBytes("Content-Disposition: form-data; name=\"name\"$lineEnd")
    dos.writeBytes("Content-Type: text/plain; charset=$charset$lineEnd")
    dos.writeBytes("Content-Length: " + fileFullName.length + lineEnd)
    dos.writeBytes(lineEnd)
    dos.writeBytes(fileFullName + lineEnd)
    dos.writeBytes(twoHyphens + boundary + lineEnd)

    // Send parameter #file
    dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"$fileFullName\"$lineEnd") // filename is the Name of the File to be uploaded

    dos.writeBytes("Content-Type: video/mp4$lineEnd")
    dos.writeBytes(lineEnd)

    dos.write(chunk)

    dos.writeBytes(lineEnd)
    dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd)

    println(dos)

    dos.flush()
    dos.close()

    val responseCode = connection.responseCode
    val responseMessage = connection.responseMessage

    val br = if (responseCode in 200..299)
        BufferedReader(InputStreamReader(connection.inputStream))
    else BufferedReader(InputStreamReader(connection.errorStream))

    println("chunkId: $chunkId, chunks: $chunks") // I print each chunk id

    val sb = StringBuilder()
    var output: String?
    while (true) {
        output = br.readLine()
        if (output.isNullOrEmpty()) break;
        sb.append(output)
    }

    println(sb.toString()) // I print response body of request

    return sb.toString()
}

For each chunk, I get response code 200, except for the first chunk.

Log:

chunkId: 0, chunks: 17
responseCode: 500
responseMessage: Internal Server Error
<html><body><h1>Whitelabel Error Page</h1><p>This application has no configured error view, so you are seeing this as a fallback.</p><div id='created'>Fri Dec 27 12:54:43 UTC 2019</div><div>There was an unexpected error (type=Internal Server Error, status=500).</div><div>500 Internal Server Error</div></body></html>
chunkId: 1, chunks: 17
responseCode: 200
responseMessage: OK
{"uploadedFiles":[{"fileId":"0deb7af7-28a8-11ea-be77-ae88efe3f04a","fileName":"3c20c268-c19a-4b13-9135-1698f2f4da11.mp4"}]}
chunkId: 2, chunks: 17
responseCode: 200
responseMessage: OK
{"uploadedFiles":[{"fileId":"0e2319fe-28a8-11ea-ab01-aa6ef33510d7","fileName":"3c20c268-c19a-4b13-9135-1698f2f4da11.mp4"}]}
chunkId: 3, chunks: 17
...

来源:https://stackoverflow.com/questions/59501141/why-i-can-not-upload-first-chunk-of-file-by-using-httpurlconnection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!