UPD: уберите CURLOPT_VERBOSE. Так как иначе verbose данные пишутся в файл и он конечно портится
ecurl_setopt($ch, CURLOPT_VERBOSE, 1);
вывод ошибок CURLOPT_STDERR тоже не стоит направлять в $fp.
Чисто по функционалу не вижу проблем в коде.
Для более точной диагностики проблемы с файлом можно сравнить их содержимое на бинарном уровне
1. скачиваете файл браузером
2. скачиваете файл curl
сравниваете содержимое через через любую diff-тулзу которая умеет сравнивать бинарные данные, например
https://www.fairdell.com/hexcmp/
будет понятно, "побился" ли файл при передаче через curl и если побился то где, в начале где идут magick bytes или может быть в конце так как не дозаписался.
А может быть у вас вообще при передаче возникли ошибки которые тоже записались в этот файл, так как указано
ecurl_setopt($ch, CURLOPT_STDERR, $fp);
Если при скачивании файла из браузера и при скачивании через curl их содержимое одинаковое, то проблема скорее в самом исходном файле.