
在php开发中,尤其是在需要将动态生成的内容(如pdf文件)作为邮件附件发送或存储到数据库时,一种常见的做法是先将数据写入服务器上的临时文件,然后从这些文件中读取内容进行后续操作,最后再删除这些临时文件。例如,将base64编码的xml字符串解码为pdf,然后将pdf作为附件发送,并将base64字符串存入数据库。
这种传统方法虽然可行,但存在以下几个明显弊端:
最初的实现方式正是采用了这种模式:从Base64编码的XML中提取字符串,写入TXT文件,再从TXT文件读取并解码为PDF,写入PDF文件,然后从PDF文件读取内容作为邮件附件,并将Base64字符串存入数据库,最后删除TXT和PDF文件。这种流程虽然功能上没有问题,但效率和安全性有待提升。
为了克服上述弊端,更优的实践是在内存中直接处理数据,避免任何磁盘写入操作。核心思想是:将所有中间数据(如Base64编码的字符串、解码后的PDF二进制内容)都存储在PHP变量中,直接传递给后续函数。
以下是优化后的实现步骤和代码示例:
立即学习“PHP免费学习笔记(深入)”;
首先,确保从原始输入中正确提取出Base64编码的PDF数据。如果原始数据是通过print_r函数输出的,需要注意print_r的输出格式并非纯粹的Base64字符串,可能包含额外的格式信息,这会影响base64_decode的正确性。理想情况下,$DHL[1]应该直接包含纯净的Base64编码字符串。
<?php
// 辅助函数:从特定标签中提取内容
function multiSplit($string) {
$output = array();
$cols = explode("<GraphicImage>", $string);
foreach($cols as $col) {
$dashcols = explode("</GraphicImage>", $col);
$output[] = $dashcols[0];
}
return $output;
}
// 假设 $output 包含原始的XML字符串
$DHL = multiSplit($output);
// 关键步骤:直接获取Base64编码的PDF内容
// 注意:如果 $DHL[1] 已经是纯净的Base64字符串,则不需要 print_r
// 如果 $DHL[1] 是一个数组或其他复杂结构,并且其某个元素是Base64字符串,需要精确提取
// 这里假设 $DHL[1] 已经是或可以被直接转换为Base64字符串
// 如果原始问题中 $DHL[1] 是 print_r 的输出,那么它不是 Base64 编码的,
// 而是 PHP 变量的字符串表示。需要确保这里获取的是实际的 Base64 编码字符串。
// 假设 $DHL[1] 包含的是原始的 Base64 字符串
$pdf_base64_string = $DHL[1]; // 确保这里是纯净的 Base64 字符串
// 为附件生成文件名
$envoi = $submission_id.'_envoi';
$filename2 = $envoi.'.pdf';
// 解码PDF内容(在内存中)
$pdf_decoded_content = base64_decode($pdf_base64_string);
// 检查解码是否成功,避免后续操作使用无效数据
if ($pdf_decoded_content === false) {
// 处理解码失败的情况,例如记录错误或抛出异常
error_log("Base64 decoding failed for PDF content.");
// 可以选择退出或采取其他恢复措施
exit("Error: Could not decode PDF content.");
}
?>现在,我们有了PDF的二进制内容($pdf_decoded_content)和其Base64编码版本($pdf_base64_string)。SendGrid等邮件库通常允许直接传入Base64编码的字符串作为附件内容,而无需读取文件。
<?php
// ... (接上文代码)
// SendGrid 部分
$email = new Mail();
$email->setFrom("sender@example.com", "Sender"); // 替换为你的发件人邮箱和名称
$email->addTo("$EMAIL", "Recipient"); // 替换为收件人邮箱和名称
$email->setSubject("Etiquettes DHL");
$email->addContent("text/html", "<strong>Hello</strong>");
// 直接使用 Base64 编码的 PDF 内容作为附件
// 注意:SendGrid 的 addAttachment 方法通常期望第一个参数是 Base64 编码的字符串
$email->addAttachment(base64_encode($pdf_decoded_content), "application/pdf", "$filename2", "attachment");
$sendgrid = new \SendGrid('YOUR_SENDGRID_API_KEY'); // 替换为你的 SendGrid API Key
try {
$response = $sendgrid->send($email);
// 可以在这里检查 $response->statusCode() 来确认邮件发送状态
} catch (Exception $e) {
error_log('Caught exception during SendGrid email sending: ' . $e->getMessage());
// 处理邮件发送失败的逻辑
}
?>注意: 邮件库的addAttachment方法可能要求附件内容是Base64编码的,或者直接接受二进制字符串。请根据你使用的具体邮件库(如PHPMailer, SwiftMailer, SendGrid等)的文档来确定addAttachment方法所需的参数格式。在SendGrid的例子中,addAttachment的第一个参数通常是文件的Base64编码内容。因此,如果$pdf_decoded_content是二进制内容,需要对其再次进行base64_encode。
将Base64编码的PDF内容直接存储到数据库中,通常将其存储在一个TEXT或BLOB类型的字段中。TEXT类型适合存储Base64字符串,而BLOB类型适合存储原始二进制数据。考虑到Base64字符串通常比原始二进制数据大约1/3,如果需要频繁读取并解码,直接存储Base64字符串可能更方便。
<?php
// ... (接上文代码)
// 插入数据库部分
try {
// 连接到数据库
$db = new PDO('mysql:dbname=jotform; host=localhost', 'user', 'password'); // 替换为你的数据库连接信息
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL 语句
$sql = 'INSERT INTO DHL (submission_id, formID, identite, email, adresse, telephone, label_envoi, commercial_invoice)
VALUES (:submission_id, :formID, :NOM, :EMAIL, :ADRESSE, :TELEPHONE, :label_envoi, :COMMERCIAL_INVOICE)';
// 准备查询
$query = $db->prepare($sql);
// 执行查询,将 Base64 编码的 PDF 字符串直接绑定到参数
$query->execute(array(
':submission_id' => $submission_id,
':formID' => $formID,
':NOM' => $NOM,
':EMAIL' => $EMAIL,
':ADRESSE' => $ADRESSE,
':TELEPHONE' => $TELEPHONE,
':label_envoi' => base64_encode($pdf_decoded_content), // 存储 Base64 编码的 PDF 内容
':COMMERCIAL_INVOICE' => $COMMERCIAL_INVOICE
));
// 成功插入后的逻辑,例如记录日志
} catch (PDOException $e) {
error_log('Error inserting data into database: ' . $e->getMessage());
// 处理数据库插入失败的逻辑
}
?><?php
// 假设 $submission_id, $output, $EMAIL, $NOM, $ADRESSE, $TELEPHONE, $COMMERCIAL_INVOICE, $formID
// 都在当前作用域内可用。
// 辅助函数:从特定标签中提取内容
function multiSplit($string) {
$output = array();
$cols = explode("<GraphicImage>", $string);
foreach($cols as $col) {
$dashcols = explode("</GraphicImage>", $col);
$output[] = $dashcols[0];
}
return $output;
}
$envoi = $submission_id.'_envoi';
$filename2 = $envoi.'.pdf'; // 附件文件名
// 步骤1: 从原始数据中提取 Base64 编码的 PDF 字符串
// 确保 $DHL[1] 包含的是纯净的 Base64 编码字符串,而非 print_r 的输出。
// 如果原始的 $output 变量是一个 XML 字符串,并且 <GraphicImage> 标签内是 Base64 编码的 PDF 数据
// 那么 multiSplit 函数的返回结果 $DHL[1] 应该就是所需的 Base64 字符串。
$DHL = multiSplit($output);
$pdf_base64_string = $DHL[1]; // 假设 $DHL[1] 是纯净的 Base64 字符串
// 步骤2: 在内存中解码 Base64 字符串,得到 PDF 的二进制内容
$pdf_decoded_content = base64_decode($pdf_base64_string);
// 检查解码是否成功
if ($pdf_decoded_content === false) {
error_log("Base64 decoding failed for PDF content.");
exit("Error: Could not decode PDF content.");
}
// 步骤3: 使用 SendGrid 发送邮件附件
// 请确保 SendGrid 库已通过 Composer 安装并可用
// composer require sendgrid/sendgrid
use SendGrid\Mail\Mail;
$email = new Mail();
$email->setFrom("sender@example.com", "Sender"); // 替换为你的发件人邮箱和名称
$email->addTo("$EMAIL", "Recipient"); // 替换为收件人邮箱和名称
$email->setSubject("Etiquettes DHL");
$email->addContent("text/html", "<strong>Hello</strong>");
// 将解码后的 PDF 二进制内容再次 Base64 编码,作为附件发送
// SendGrid 的 addAttachment 期望第一个参数是 Base64 编码的文件内容
$email->addAttachment(base64_encode($pdf_decoded_content), "application/pdf", "$filename2", "attachment");
$sendgrid = new \SendGrid('YOUR_SENDGRID_API_KEY'); // 替换为你的 SendGrid API Key
try {
$response = $sendgrid->send($email);
// 可以在这里检查 $response->statusCode() 来确认邮件发送状态
// 例如:if ($response->statusCode() >= 200 && $response->statusCode() < 300) { /* 成功 */ }
} catch (Exception $e) {
error_log('Caught exception during SendGrid email sending: ' . $e->getMessage());
// 处理邮件发送失败的逻辑
}
// 步骤4: 将 Base64 编码的 PDF 字符串存储到数据库
try {
// 连接到数据库
$db = new PDO('mysql:dbname=jotform; host=localhost', 'user', 'password'); // 替换为你的数据库连接信息
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL 语句
$sql = 'INSERT INTO DHL (submission_id, formID, identite, email, adresse, telephone, label_envoi, commercial_invoice)
VALUES (:submission_id, :formID, :NOM, :EMAIL, :ADRESSE, :TELEPHONE, :label_envoi, :COMMERCIAL_INVOICE)';
// 准备查询
$query = $db->prepare($sql);
// 执行查询,将 Base64 编码的 PDF 字符串直接绑定到参数
$query->execute(array(
':submission_id' => $submission_id,
':formID' => $formID,
':NOM' => $NOM,
':EMAIL' => $EMAIL,
':ADRESSE' => $ADRESSE,
':TELEPHONE' => $TELEPHONE,
':label_envoi' => base64_encode($pdf_decoded_content), // 存储 Base64 编码的 PDF 内容
':COMMERCIAL_INVOICE' => $COMMERCIAL_INVOICE
));
// 成功插入后的逻辑
} catch (PDOException $e) {
error_log('Error inserting data into database: ' . $e->getMessage());
// 处理数据库插入失败的逻辑
}
?>通过在内存中直接处理数据流,我们成功地消除了在服务器上创建、管理和删除临时文件的需求。这种“无文件”操作方式不仅提升了应用程序的性能和安全性,还简化了代码逻辑,减少了潜在的错误点。在开发涉及文件附件和数据存储的应用时,优先考虑内存处理是值得推荐的最佳实践。
以上就是PHP:优化邮件附件发送与数据库存储,告别临时文件困扰的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号