"Где мои байты?" или Изучение характеристик длины файла

Это продолжение моего вопроса о загрузке файлов в куски. Объяснение будет довольно большим, поэтому я попытаюсь разделить его на несколько частей.

1) Что я пытался сделать?

Я создавал диспетчер загрузки для приложения Window-Phone. Во-первых, я попытался решить проблему загрузки  большие файлы (объяснение в предыдущем вопросе). Нет. Я хочу добавить функцию "возобновляемой загрузки".

2) Что я уже сделал.

В настоящий момент у меня есть хорошо работающий менеджер загрузок, который позволяет обойти ограничение по ОЗУ Windows Phone.  Сюжет этого менеджера заключается в том, что он позволяет загружать небольшие фрагменты файла, используя заголовок HTTP Range.

Быстрое объяснение того, как это работает:

Файл загружается в куски постоянного размера. Позвольте называть этот размер "delta". После загрузки фрагмента файла,  он сохраняется в локальном хранилище (жесткий диск, на WP он называется изолированным хранилищем) в режиме добавления (так что загруженный массив байтов  всегда добавляется в конец файла). После загрузки одного фрагмента оператор

if (mediaFileLength >= delta) // mediaFileLength is a length of downloaded chunk

. Если это правда, то  означает, что что-то осталось для загрузки, и этот метод вызывается рекурсивно. В противном случае это означает, что этот кусок  был последним, и загрузить его не удавалось.

3) Какая проблема?

До тех пор, пока я не использовал эту логику при одноразовых загрузках (в одно время я имею в виду, когда вы начинаете загрузку файла и дождитесь завершения загрузки)  это сработало хорошо. Однако я решил, что мне нужно "возобновить загрузку". Итак, факты:

3.1) Я знаю, что размер куска файла является константой.

3.2) Я знаю, когда файл полностью загружен или нет. (что косвенным результатом моей логики приложения,  не устанет от вас объяснением, просто предположите, что это факт)

В предположении этих двух утверждений я могу доказать, что количество загруженных кусков равно   (CurrentFileLength)/дельта. Где CurrentFileLenght - размер уже загруженного файла в байтах.

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

    // Check file size
    using (IsolatedStorageFileStream fileStream = isolatedStorageFile.OpenFile("SomewhereInTheIsolatedStorage", FileMode.Open, FileAccess.Read))
    {
      int currentFileSize = Convert.ToInt32(fileStream.Length);
      int currentFileChunkIterator = currentFileSize / delta;
    }

И что я вижу в результате? Длина загруженного файла равна байту 2432000 ( delta = 304160; общий размер файла 4,5 МБ, мы скачали только половина его). Таким образом, результат  примерно 7,995. (на самом деле он имеет длинный/int-тип, поэтому он 7 и должен быть 8 вместо!) Почему это происходит?  Простая математика говорит нам, что длина файла должна быть 2433280, поэтому данное значение очень близко, но не равно.

Дальнейшие исследования показали, что все значения, заданные из fileStream.Length, неточны, но все близки.

Почему это происходит? Я точно не знаю, но, возможно, значение .Length берется где-то из метаданных файла.  Возможно, такое округление является нормальным для этого метода. Возможно, когда загрузка была прервана, файл не был полностью сохранен... (нет, это действительно фантастично, этого не может быть)

Итак, проблема установлена ​​- это "Как определить количество загруженных кусков" . Вопрос в том, как его решить.

4) Мои мысли о решении проблемы.

Моя первая мысль заключалась в том, чтобы использовать математику здесь. Установите некоторое epsilon-neiborhood и используйте его в инструкции currentFileChunkIterator = currentFileSize / delta;. Но это потребует, чтобы мы помнили о ошибках типа я и типа II (или ложных тревогах и промахах, если вам не нравятся термины статистики.) Возможно, ничего не осталось для загрузки. Кроме того, я не проверял, если предполагается, что разница предоставленного значения и истинного значения будет постоянно расти или будут циклические колебания. С небольшими размерами (около 4-5 МБ) я видел только рост, но это ничего не доказывает.

Итак, я прошу о помощи здесь, так как мне не нравится мое решение.

5) Что я хотел бы услышать в качестве ответа:

Что вызывает разницу между реальным значением и полученным значением?

Есть ли способ получить истинное значение?

Если это не так, мое решение подходит для этой проблемы?

Существуют ли другие лучшие решения?

P.S. Я не буду устанавливать тег Windows-Phone, потому что я не уверен, что эта проблема связана с ОС. Я использовал изолированный инструмент хранения чтобы проверить размер загруженного файла, и он показал мне то же самое, что и полученное значение (я сожалею о русском языке на снимке экрана):

File size is wrong image

Ответ 1

Слышали ли вы анекдот о noob-программисте и 10 гуру-программистах? Гуру-программисты пытались найти ошибку в своем решении, и Нооб уже нашел его, но не сказал об этом, так как это было что-то глупое, мы боялись, чтобы нас смеяли.

Почему я это вспомнил? Потому что ситуация похожа.

Объяснение моего вопроса было очень тяжелым, и я решил не упоминать некоторые небольшие аспекты, которые я был уверен, правильно работал. (И они действительно работали правильно)

Одним из этих небольших аспектов был факт, что загруженный файл был зашифрован через дополнение AES PKCS7. Ну, дешифрование работало правильно, я знал это, так почему я должен это упоминать? И я этого не сделал.

Итак, тогда я попытался выяснить, что именно вызывает ошибку с последним куском. Самая надежная версия была связана с проблемами с буферизацией, и я попытался найти, где я оставляю недостающие байты. Я тестировал снова и снова, но я не мог найти их, поскольку каждый кусок экономя без потерь. И однажды я понял:

Нет ложки

Ошибка.

Какой смысл AES PKCS7? Наилучшим образом, главным из них является то, что он расшифровывает файл меньше. Не так много, только в 16 байт. И это было рассмотрено в моем методе дешифрования и методе загрузки, поэтому проблем не должно быть, правильно?

Но что происходит, когда процесс загрузки прерывается? Последний кусок будет правильно сохранен, ошибок с буферизацией и других не будет. И затем мы хотим продолжить загрузку. Количество загруженных кусков будет равно currentFileChunkIterator = currentFileSize / delta;

И здесь я должен спросить себя: "Почему вы пытаетесь сделать что-то такое глупо?"

"Ваш загруженный размер одного фрагмента не дельта. Фактически, он меньше дельта". (расшифровка делает кусок меньше 16 байт, помните?)

Дельта сама состоит из 10 равных частей, которые дешифруются. Поэтому мы должны делить не на дельту, а на (delta-16 * 10), которая равна (304160-160) = 304000.

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

2432000/304000 = 8. Подождите... OH SHI ~


Итак, это конец истории.

Вся логика решения была правильной.

Единственная причина, по которой это не удалось, было моей мыслью, что по какой-то причине загруженный расшифрованный размер файла должен быть таким же, как сумма загруженных зашифрованных фрагментов.

И, конечно, поскольку я не упоминал о расшифровке (он упоминается только в предыдущем вопросе, который связан только), никто из вас не может дать мне правильный ответ. Я ужасно сожалею об этом.

Ответ 2

Я отвечаю на ваше обновление:

Это мое понимание до сих пор: длина, фактически записанная в файл, больше (округляется до следующего 1KiB), чем вы на самом деле писали. Это приводит к ошибочному предположению о том, что "file.Length == amount загружено".

Одним из решений было бы отслеживать эту информацию отдельно. Создайте структуру метаданных (которая может сохраняться с использованием одного и того же механизма хранения), чтобы точно отслеживать, какие блоки были загружены, а также весь размер файла:

[DataContract] //< I forgot how serialization on the phone works, please forgive me if the tags differ
struct Metadata
{
     [DataMember]
     public int Length;
     [DataMember]
     public int NumBlocksDownloaded;
}

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

изменить

Конечно, вам нужно будет изменить свой код из простого приложения, чтобы переместить позицию потока в правильный блок, прежде чем записывать данные в поток:

 file.Position = currentBlock * delta;
 file.Write(block, 0, block.Length);

Ответ 3

Продолжайте мой комментарий..

Исходный размер файла, как я понимаю из вашего описания, составляет 2432000 байт.
Размер Chunk установлен в 304160 байт (или 304160 на "дельта" ).

Итак, машина, которая отправила файл, смогла заполнить 7 кусков и отправила их.
Получающая машина теперь имеет 7 x 304160 байт = 2129120 байт.

Последний фрагмент не будет заполнен до конца, так как для его заполнения недостаточно байт, поэтому он будет содержать: 2432000 - 2129120 = 302880, что меньше 304160

Если вы добавите числа, вы получите 7x304160 + 1x302880 = 2432000 байт Таким образом, в соответствии с этим исходный файл полностью передан в пункт назначения.

Проблема в том, что вы вычисляете 8x304160 = 2433280, настаивая на том, что даже последний кусок должен быть заполнен полностью - но с чем??? и почему?

В смиренном... ты заперт в каком-то замешательстве в математике или я неправильно понял твою проблему?
Пожалуйста, ответьте: Каков размер исходного файла и какой размер будет получен на другом конце? (Итоги!)

Ответ 4

Как возможная ошибка. Не забудьте проверить, был ли файл изменен во время запросов. Специально в течение длительного времени между ними, которые могут возникать при паузе/возобновлении. Ошибка может быть большой, например, файл изменяется до небольшого размера, и ваш счет становится "erronic", а файл имеет тот же размер, но с измененным содержимым, это оставит поврежденный файл.