I have a webpage in PHP that lets me access an IMAP account. I can fetch the messages fine. But, when I go to download attachments, Firefox opens the download dialog, but immediately after pops an error window, saying: "W227LcEc.png.part could not be saved, because the source file could not be read." And it doesn't let me click the "Save" button in the download dialog.
However, I checked the Network tab of the Developer Tools. The file was correctly received (return code 200), the size is exactly as expected, and in the Response tab I can see the contents of the file, encoded in Base64. If I take this content, and save into a text file, decode Base64, I have a perfectly working file.
I've googled, found advices about header directives, but to no avail to solve the problem.
I'm including only relevant part of codes.
This is the HTML:
<div onclick="download('1234','ABCEDF')">clinical-guidelines-2024-en.pdf</div>
In this example, '1234' would be a unique identifier to a specific email, and 'ABCEDF' a unique identifier to its attachment (in this example, the mencioned PDF).
Now, the javascript code:
function download(message_id,attachment_id) {
var link_el = document.createElement("A");
link_el.href = 'https://my.server.com/webservice.php?message_id='+message_id+'&attachment_id='+attachment_id;
link_el.click();
}
And this is the relevant part of the PHP code in webservice.php:
$struct = imap_fetchstructure($imap_instance,$message_id,FT_UID);
...
// Then I process $struct, loop through $struct->parts, until I find the correct attachment
...
// When attachment is found:
$type = $struct->parts[$i]->type;
$subtype = strtolower($struct->parts[$i]->subtype);
$filesize = $struct->parts[$i]->bytes;
$encoding = $struct->parts[$i]->encoding;
$attachment_partno = $i + 1;
$data = imap_fetchbody($imap_instance,$message_id,$attachment_partno,FT_UID | FT_PEEK);
if ($encoding == 4) {
$data = quoted_printable_decode($data);
} elseif ($encoding == 3) {
$data = base64_decode($data);
}
$mimetypes = array(0 => 'text',1 => 'multipart',2 => 'message',3 => 'application',4 => 'audio',5 => 'image',6 => 'video',7 => 'model',8 => 'other');
header('Content-Type: '.$mimetypes[$type].'/'.$subtype);
// Force the browser to download the file as an attachment
// The $filename variable is processed from the PARAMETERS and DPARAMETERS values, and decoded with imap_mime_header_decode(). I'm omitting all this for brevity, since this part is working all right.
header('Content-Disposition: attachment; filename="'.$filename.'"');
// Specify the file size for download progress indicators
header('Content-Length: '.$filesize);
// Prevent caching of the file
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Expires: 0');
echo $data;
Clicking on the link, a request to the server is correctly made, and the answer also is correct. Here are the response headers:
HTTP/2 200
date: Tue, 18 Nov 2025 15:07:57 GMT
content-type: application/pdf
content-length: 408416
access-control-allow-origin: UNSET
content-disposition: attachment; filename="clinical-guidelines-2024-en.pdf"
cache-control: must-revalidate, post-check=0, pre-check=0
pragma: public
expires: 0
strict-transport-security: max-age=63072000; includeSubDomains; preload
permissions-policy: accelerometer=(), ambient-light-sensor=(), attribution-reporting=(), autoplay=(), bluetooth=(), browsing-topics=(), camera=(), compute-pressure=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), identity-credentials-get=(), idle-detection=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), otp-credentials=(), payment=(), picture-in-picture=(), publickey-credentials-create=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), storage-access=(), usb=(), web-share=(), window-management=(), xr-spatial-tracking=(), aria-notify=(), captured-surface-control=(), cross-origin-isolated=(), deferred-fetch=(), deferred-fetch-minimal=(), on-device-speech-recognition=(), summarizer=(), wildcards=(), interest-cohort=()
x-dns-prefetch-control: off
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
content-security-policy: object-src 'none'; form-action 'self'; frame-ancestors 'self';
X-Firefox-Spdy: h2
My platform is Windows 10, and I'm using Firefox 144.0 64-bit.
Any help is appreciated!