addAttachment v Nette/Mail – název přílohy?
- Martin Mates
- Člen | 179
Pokud přidám do mailu PDF přílohu
$mail->addAttachment(WWW_DIR .'test.pdf', null, 'application/pdf');
přijde v mailu příloha sice v pořádku, ale s názvem „Mail
Attachment.pdf“.
Můžu to nějak ovlivnit, jaký bude mít příloha název? Druhý parametr metody addAttachment moc nechápu.
Díky!
- Patrik Votoček
- Člen | 2221
Zajímavé příloha by totiž měla přijít s názvem
test.pdf
viz https://api.nette.org/…ail.php.html#300
- Martin Mates
- Člen | 179
Včera jsem to ještě zkoušel a problém bude v nějakém limitu názvu přílohy. Když je název moc dlouhý, tak to přijde jako „Mail Attachment“. Nevím přesně jaký ten limit je. Každopádně to nebude záležitost Nette. Díky!
- MzK
- Člen | 127
Škoda že nejde jako to nefunguje takto:
<?php
$mail->addAttachment('http://www.server.cz/test.pdf');
?>
je s tím někde problém? jakmile zadám soubor s přílohou, email se odešle, ale nikdy ho nepřijmu.. Žádná chyba se při odesílání nevypisuje, může být na server zakázáno odesílání emialů s přílohou?
- ptacek.pavel
- Člen | 27
Narazil jsem na tento problém také při odesíláni .pdf souborů – soubor mailem dorazil jako „Unnamed attachment“ bez přípony. Při snaze to vyřešit jsem narazil na zajímavý efekt odesílání příloh, který se děje pouze pokud je název souboru vč. přípony delší než cca 40 znaků. Během toho co jsem debuggoval, narazil jsem na efekt použití classy Nette\Mail\MailMimePart, který chybně escapuje hlavičku Content-Disposition v případě, že se jedná o delší název souboru.
Pokud nahodím dlouhý soubor, pak mi v mailu dorazila hlavička takováto:
----------51beff7ff9ea7f871ceccb336e59173c
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment
(všimněte si, že tam není jméno souboru)
Což je způsobeno tím, že samotný MailMimePart definuje hlavičku takto:
MIME-Version: 1.0
X-Mailer: Nette Framework
Date: Tue, 12 Apr 2011 15:18:17 +0200
From: test
Message-ID: <5bc872177f38759b94033ffcae41912e@manghi.loc>
Content-Type: multipart/mixed;
boundary="--------f814f502c0f8e76c7f4c72054c3611cd"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: =?UTF-8?B?YXR0YWNobWVudDsgZmlsZW5hbWU9InRvaGw=?=
=?UTF-8?B?ZS1qZS1zdXBlci1kbG91aGVqLXV0ZjgtbmF6ZXYtcGRma2Eta3RlcmU=?=
=?UTF-8?B?ai1ieS1tZWwtZGVsYXQtcHJvYmxlbXktYS10YWstdG8tdGFraGxlLXA=?=
=?UTF-8?B?b2ptZW5vdmF2YW0tZXNjcnp5YWllZHRuLXBkZi5wZGYi?=
Zkoumal jsem i stránku http://www.velocityreviews.com/…lenames.html kde řeší, jak „správně“ zalamovat hlavičku s dlouhými názvy souborů tak, aby to mail server & klient sežral v pohodě.
U mě escapování nebylo potřeba, neboť jsem všechny názvy souborů nejdříve prohnal skrz Nette\Utils\String::webalize a zachoval příponu (jinak vznikal pro změnu bordel ve filesystemu → soubory jsou přílohy z kontaktního formuláře). Nakonec jsem si doplnil hotfix, který zabraňuje escapování (prostě proto, že u mě to není potřeba) – ale není to elegantní řešení a proto postuju zde, s prosbou o nějaké lepší :-)
Nette\Mail\MailMimePart 269:
<?php
/**
* Returns encoded message.
* @return string
*/
public function generateMessage()
{
$output = '';
$boundary = '--------' . md5(uniqid('', TRUE));
foreach ($this->headers as $name => $value) {
// Hotfix by Pavel Ptacek -> I don't care about wrapping up,
// somehow (elsewhere in the file?) it gets wrapped automatically
// The key is to prevent utf escape of the filename itself
if($name === 'Content-Disposition') {
$output .= $name . ': ' . $value;
$output .= self::EOL;
continue;
}
$output .= $name . ': ' . $this->getEncodedHeader($name);
if ($this->parts && $name === 'Content-Type') {
$output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
}
$output .= self::EOL;
}
$output .= self::EOL;
$body = (string) $this->body;
if ($body !== '') {
switch ($this->getEncoding()) {
case self::ENCODING_QUOTED_PRINTABLE:
$output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
break;
case self::ENCODING_BASE64:
$output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
break;
case self::ENCODING_7BIT:
$body = preg_replace('#[\x80-\xFF]+#', '', $body);
// break intentionally omitted
case self::ENCODING_8BIT:
$body = str_replace(array("\x00", "\r"), '', $body);
$body = str_replace("\n", self::EOL, $body);
$output .= $body;
break;
default:
throw new \InvalidStateException('Unknown encoding');
}
}
if ($this->parts) {
if (substr($output, -strlen(self::EOL)) !== self::EOL) $output .= self::EOL;
foreach ($this->parts as $part) {
$output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
}
$output .= '--' . $boundary.'--';
}
return $output;
}
?>
edit: Ještě doplním, že problém není v samotném escapování názvu souboru, problém je v tom, že hlavička Content-disposition (resp. její parametr filename="") musí být na jednom řádku. Následující hlavičky by tedy měli být OK:
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="test.pdf"
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="tohle je hrozne dlouhe jmeno attachmentu ktere nema zalomeni a tim padem projde.pdf"
Oproti tomu problém způsobuje tento formát:
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="tohle je hrozne dlouhe jmeno
attachmentu ktere ma zalomeni a tim
padem to dojde jako nepojmenovana priloha BEZ PRIPONY.pdf"
Můj hotfix nahoře funguje tedy jenom a pouze proto, že jméno souboru nemá mezeru → spíše celý parametr filename=„…“ nemá mezeru. A tím pádem nedojde k zalomení, na základě čehož to všechny mail servery & klienti vezmou bez problému.
Editoval ptacek.pavel (12. 4. 2011 16:03)
- 22
- Člen | 1478
chápu to dobře viz.: https://api.nette.org/…age.php.html#302
ve $file je cesta k souboru, to se využije pro získání $content, pokud je
NULL, ze kterého se získá $contentType a s $file se vezme konec, jak název
souboru.
Takže asi jsi se akorát upsal $filename ⇒ $file ?
Editoval 22 (27. 4. 2011 0:43)
- kravčo
- Člen | 721
Áno, bol to preklep. Presnejšie, basename($file)
je názov
prílohy, $content
jej binárny obsah a $contentType
jej MIME typ (štvrtý parameter nás teraz netrápi). Akonáhle je
$content
a/alebo $contentType
NULL
ový,
prirodzeným spôsobom sa dopočíta:
- do
$content
sa pokúsi načítať obsah súboru$file
- do
$contentType
detekuje typ z obsahu$content
(nie nutne z obsahu súboru$file
)
- 22
- Člen | 1478
tvoje řešení:
<?php
$file = 'pick-your-name.zip';
$content = file_get_contents(__DIR__ . '/files/'
. 'very long named file that needs to be attached to an email.zip');
$contentType = 'application/zip';
$mail->addAttachment($file, $content, $contentType);
moje vize:
$mail->addAttachment($path, $content = NULL, $contentType = NULL, $publicName = NULL);
pokud je cesta a zbytek NULL, tak všechno obslouží Nette, pokud zadám $publicName, tak se nevolá basename(), ale dosadí se 4. parametr
Editoval 22 (27. 4. 2011 15:18)
- petr.pavel
- Člen | 535
@pjoter: Tohle vlákno je asi už mimo. Koukám do zdrojáku a vidím, že když dodáš vlastní $content, tak $file může být cokoliv – ten tvůj vlastní název přílohy.