InputStream пропускает несколько байтов при чтении файла через FTPAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 InputStream пропускает несколько байтов при чтении файла через FTP

Сообщение Anonymous »

Я пытаюсь загрузить ZIP-файл на Android через FTP.

Проблема в том, что иногда в полученном файле отсутствуют некоторые байты в конце.
Этого не происходит при использовании InputStream/

Код: Выделить всё

OutputStream
в локальных файлах.

Это не проблема файла: я тестировал множество разных файлов с одинаковыми результатами.

Это не проблема сервера: Я тестировал на двух разных - одинаковые результаты.

Это даже не похоже на проблему с подключением: тестировал на двух разных соединениях, одна и та же проблема.

И более того, похоже, это не проблема. проблема конкретно с Apache FTPClient: я пробовал старый ftp4j и выдает одинаково недостающие байты ближе к концу файла.
Насколько я понимаю, это InputStream.read(), который не выполняет свою работу должным образом, пропуская один байт в начале файла. конец некоторых буферов.

Код: Выделить всё

import org.apache.commons.net.ftp.FTPClient

fun downloadFile(remotePath: String, outputFile: File) {
val client = FTPClient()
client.connect("ftp.example.com")
client.login("username", "password")
client.enterLocalPassiveMode()
println(client.getSize(remotePath)) // Say 100000 bytes
//client.setFileType(FTP.BINARY_FILE_TYPE) // Doesn't matter
client.retrieveFileStream(remotePath).use { inputStream ->
outputFile.outputStream().use { outputStream ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE) // 8 * 1024
var bytes: Int
while (inputStream.read(buffer).also { bytes = it } != -1) {
outputStream.write(buffer, 0, bytes)
}
}
}
client.completePendingCommand()
client.disconnect()
println(outputFile.length()) // Usually 100000, other times 99999 or less bytes
}

Код: Выделить всё

lifecycleScope.launch(Dispatchers.IO) {
downloadFile("/path/to/input.zip", File(cacheDir, "output.zip"))
}
Файлом, который будет использоваться для тестирования, может быть любой файл, превышающий размер буфера, а также простой файл ASCII.

Очевидно, что файл большего размера и с большей вероятностью будет поврежден.< /p>
Как правильно получить двоичный файл через FTP?
Изменить
Эта проблема затрагивает только эмуляторы. Я не могу воспроизвести его на физических устройствах.
Я тестировал загрузку файла размером 300 КБ, заполненного только 012345678901234567890123456789012345678901234567890123456789...
Я добавил проверки, чтобы увидеть, есть ли какой-то байт отсутствует внутри или в конце буфера:

Код: Выделить всё

fun downloadFile(remotePath: String, outputFile: File) {
...
println("Input size: " + client.getSize(remotePath))
client.retrieveFileStream(remotePath).use { inputStream ->
outputFile.outputStream().use { outputStream ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes: Int
var count = 0
var lastPrev: Byte = '9'.code.toByte()
var firstNext: Byte
var prevBytes = 0
var totalBytes: Long = 0
while (inputStream.read(buffer).also { bytes = it } != -1) {
count++
firstNext = buffer[0]
checkSequence(buffer, bytes, count)
if (!checkJunction(lastPrev, firstNext))
println(
"Incoherent start of buffer $count: " +
"${lastPrev.toInt().toChar()}-${firstNext.toInt().toChar()} ($prevBytes, $bytes)"
)
lastPrev = buffer[bytes - 1]
prevBytes = bytes
totalBytes += bytes
outputStream.write(buffer, 0, bytes)
}
if (!checkJunction(lastPrev, '0'.code.toByte()))
println("Missing end of buffer $count: ${lastPrev.toInt().toChar()} ($prevBytes)")
println("Total $count buffers: $totalBytes")
}
}
client.completePendingCommand()
client.disconnect()
println("Final size: ${outputFile.length()}") // Always equal to totalBytes
}

private fun checkSequence(buffer: ByteArray, bytes: Int, count: Int) {
val nine = '9'.code.toByte()
val zero = '0'.code.toByte()
for (i in 0 until bytes - 1) {
val ok = if (buffer[i] == nine) buffer[i + 1] == zero else buffer[i + 1] == buffer[i].inc()
if (!ok) println(
"Broken buffer $count: ${buffer[i].toInt().toChar()}${buffer[i + 1].toInt().toChar()} ($i of $bytes)"
)
}
}

private fun checkJunction(lastPrev: Byte, firstNext: Byte): Boolean {
return if (lastPrev == '9'.code.toByte()) firstNext == '0'.code.toByte()
else firstNext == lastPrev.inc()
}
Обычно вывод выглядит следующим образом:

Размер ввода: 300000

Всего 68 буферы: 300000

Окончательный размер: 300000

Но иногда может быть что-то вроде этого:

Размер ввода: 300000

Некогерентное начало буфера 176: 8-0 (1438, 1)

Некогерентное начало буфера 179: 8-0 (1439, 1440)

Некогерентное начало буфера 181: 8-0 (2879, 1440)

Некогерентное начало буфера 184: 8-0 (1439, 1)

Разбитый буфер 185: 80 (4317 из 5758)

Несвязное начало буфера 187: 8-0 (2879, 1)

Отсутствует конец буфера 191: 8 (1439)

Всего 191 буферов: 299993

Окончательный размер: 299993

Проблема всегда заключается в том, что в конце или внутри некоторых буферов не хватает одного байта.

Затронутые буферы находятся ближе к концу входного потока.

Всегда отсутствует цифра 9, в том числе и внутри буфера.

Подробнее здесь: https://stackoverflow.com/questions/792 ... le-via-ftp
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»