DownloadManager загружает файлы размером более 2,1 ГБ

Я работаю над приложением, и одной из функций, над которыми я работаю, является загрузка некоторых двоичных файлов. Некоторые из них действительно большие (более нескольких мегабайт). Загрузка проходит нормально, если размер файла меньше 2 ГБ.

Я застрял на файле размером 3,2 ГБ, поскольку я получаю обновления прогресса (я объединяю DownloadManager для обновлений прогресса), но когда загрузка завершается, файл отсутствует на пути к целевому файлу. Запрашивая DownloadManager для этого идентификатора загрузки, я получаю STATUS_FAILED и причину ERROR_UNKNOWN — самая любимая информация об ошибках, которую только можно пожелать!

Странно то, что это отображается на большинстве устройств, но на некоторых (например, Samsung SG 4 Active OS 4.2.2 и LG Nexus 5 OS 4.4.2) оно не отображается.

Проведя дополнительное расследование, я обнаружил, что это, похоже, ошибка в Android. DownloadManager реализация. Кажется, что реализация Android хранит количество загрузок как целое число, но когда это количество превышает Integer.MAX_VALUE, загрузка завершается как неудачная.

Я думаю заменить использование DownloadManager сервисом переднего плана, но я бы еще не сдался ....

Ребята, вы сталкивались с этим, и если да, то как вы это исправили? Есть ли обходной путь для использования DownloadManager в версиях до 4.2.2, чтобы я мог загружать более 2,1 ГБ на файл?


person gunar    schedule 12.02.2014    source источник
comment
посмотрите Ion от Koush: github.com/koush/ion это очень здорово и может работать хорошо с большими файлами   -  person josebama    schedule 12.02.2014
comment
@josebama: фактическая загрузка отключена от пользовательского интерфейса? Я имею в виду, что если пользователь повернет устройство, выйдет из приложения, а затем снова войдет, продолжится ли загрузка и будет ли она соответствовать всему пользовательскому интерфейсу?   -  person gunar    schedule 12.02.2014
comment
Я не могу подтвердить, но это должно быть   -  person josebama    schedule 12.02.2014
comment
Ну, 2,1 ГБ достаточно близко к 2^31. Я могу догадаться, почему   -  person    schedule 12.02.2014
comment
@taytay: Чтобы быть грубым: из-за умника, который сделал поле счетчика int, а не long в реализации DownloadManager!   -  person gunar    schedule 12.02.2014
comment
Когда он достигает 4 ГБ, у вас, вероятно, могут возникнуть проблемы с файловой системой. Поскольку вы, вероятно, должны поддерживать загрузку на внешнюю карту таких больших файлов, вполне вероятно, что вы упираетесь в ограничение размера 4 ГБ, когда карты отформатированы в FAT.   -  person PMF    schedule 15.02.2014
comment
@PMF: Не могли бы вы уточнить, что вы подразумеваете под 4GB issue?   -  person gunar    schedule 15.02.2014
comment
Что ж, файловая система FAT32 (которая до сих пор широко используется, особенно для SD-карт и других сменных носителей) не поддерживает файлы размером более 4 ГБ.   -  person PMF    schedule 15.02.2014
comment
Почему вы не можете реализовать свой собственный сервис загрузки файлов http.   -  person Vishal Santharam    schedule 17.02.2014
comment
Поскольку DownloadManager позаботится обо мне в следующем: возобновить загрузку при восстановлении подключения, загружать по определенному каналу (Wi-Fi или мобильный), продолжить загрузку, если приложение будет убито.   -  person gunar    schedule 17.02.2014
comment
Затем сделайте пользовательскую версию из самой реализации DownloadManager. grepcode.com/file/repository.grepcode.com/java/ext/   -  person Shakti    schedule 18.02.2014
comment
@ShaktiThakran: проблема не в методе DownloadManager, поскольку enqueue просто ставит запрос на загрузку в очередь с использованием системы ContentProvider. Я считаю, что проблема заключается в фактическом компоненте загрузчика, и я также считаю, что это другой процесс.   -  person gunar    schedule 18.02.2014


Ответы (5)


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

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

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

Если вы планируете загрузить файл на SD-карту, файловой системой по умолчанию является FAT32. В этой файловой системе существует ограничение в 4 ГБ на файл.

person Ajay S    schedule 20.02.2014
comment
Вы обобщили то, что все остальные сказали до сих пор :) Но я не думаю, что вы предоставили что-то новое. - person gunar; 20.02.2014
comment
Это единственный способ пройти через это, я пока не читал других ответов. Не думаю, что я их скопировал, просто по возможности я их просто написал и не знаю, почему вам показалось, что я не привел чего-то нового. - person Ajay S; 20.02.2014
comment
Принятие этого ответа, даже если он на самом деле не отвечает на него, потому что правдоподобного ответа нет. В конце концов я написал свой собственный менеджер загрузок, но этот ответ настолько близок, насколько это возможно. Сначала я хотел принять ответ от ksasq, но не смог его проверить, так как у меня нет сервера, способного обрабатывать разрозненный контент. Решение Range работает для меня, так как сервер его поддерживает. Спасибо! - person gunar; 21.02.2014

Судя по исходному коду Android, эта проблема была решена в JB-MR2.

Кажется, что единственный способ обойти это на более старых версиях платформы — изменить сервер таким образом, чтобы он использовал кодирование передачи по частям [1] для этих больших ресурсов. В этом случае этот диспетчер загрузки будет игнорировать и не пытаться анализировать заголовок Content-Length.

[1] http://en.wikipedia.org/wiki/Chunked_transfer_encoding

person ksasq    schedule 20.02.2014
comment
Спасибо за ответ (+1), но, как уже было сказано, к сожалению, это не вариант на стороне сервера. Во всяком случае, мне интересно, пробовал ли кто-нибудь этот подход и подтвердил, работает ли он - person gunar; 20.02.2014
comment
У этого ответа пока 5 голосов и нет подтверждения, что это действительно работает. У меня нет доступа к серверу, который поддерживает ответ на фрагментированный контент, чтобы проверить его. - person gunar; 21.02.2014

Из этого есть один очевидный результат:

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

Итак, я думаю, что вашим самым простым решением было бы принудительно установить минимальный уровень SDK на JB-MR2, потому что @ksasq упомянул, что эта проблема была решена.

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

К сожалению, поиск в Google показал android-download-manager от yingyixu, последний раз обновленный в 2012 году.

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

person Sherif elKhatib    schedule 20.02.2014
comment
Загрузка — это только одна из функций приложения. Я не могу установить минимальный уровень JB-MR2, потому что потеряю остальную часть рынка. - person gunar; 21.02.2014

Вы можете решить эту проблему, разделив файл на более мелкие zip-файлы. Следующий шаг — присоединиться к ним на цели, я нашел ->это‹- это может тебе помочь. Если вы не будете сжимать файл (опция только разделения), у вас должна быть хорошая производительность. Другая проблема заключается в том, что вам потребуется в два раза больше места для хранения. Вы можете загружать файлы меньшего размера, около 100 МБ, записывать их в объединенный буфер и удалять из файловой системы, что позволит сэкономить место.

person Prettygeek    schedule 17.02.2014
comment
Спасибо за ответ (+1), но это не вариант на стороне сервера. - person gunar; 17.02.2014

Вы также можете использовать фиксированную версию DownloadManager, измените структуру пакета на свою и используйте эту версию вместо системной. В конце концов вам нужно импортировать некоторые классы из исходного пакета android.app. Затем зарегистрируйте свою реализацию как службу.

person ChrLipp    schedule 21.02.2014
comment
Фактическая загрузка не выполняется DownloadManager; системное приложение делает это от имени вызывающего приложения. Чтобы проверить это, запустите загрузку из примера приложения, принудительно закройте приложение, и при открытии приложения «Диспетчер загрузок» вы увидите, что загрузка выполняется. - person gunar; 21.02.2014