在PHP中,如果你需要从本地文件导入数据到MySQL数据库,通常会使用 LOAD DATA LOCAL INFILE 语句。然而,由于安全性原因,该功能在某些环境下可能被禁用或限制。PHP提供了一个替代方式——通过 mysqli::set_local_infile_handler() 方法自定义读取本地文件的行为。这不仅增强了安全性,还赋予开发者更大的灵活性。
本文将详细讲解如何使用 set_local_infile_handler() 函数实现MySQL的本地文件导入,并配合一个完整的实例进行说明。
mysqli::set_local_infile_handler() 是PHP中 mysqli 扩展提供的方法,用于在执行 LOAD DATA LOCAL INFILE 时,通过用户自定义的回调函数读取本地文件内容。
基本语法:
<span><span>mysqli::</span><span><span class="hljs-variable constant_">set_local_infile_handler</span></span><span> ( </span><span><span class="hljs-keyword">callable</span></span><span> </span><span><span class="hljs-variable">$read_func</span></span><span> ) : </span><span><span class="hljs-keyword">bool</span></span><span>
</span></span>
其中 $read_func 是一个用户定义的函数,它用于处理数据的读取和传输。
在使用之前,需要确保以下几项被正确配置:
MySQL服务器端启用了 local_infile:
<span><span><span class="hljs-keyword">SHOW</span></span><span> VARIABLES </span><span><span class="hljs-keyword">LIKE</span></span><span> </span><span><span class="hljs-string">'local_infile'</span></span><span>;
</span></span>
若为 OFF,需在服务器配置文件 my.cnf 中设置:
<span><span><span class="hljs-section">[mysqld]</span></span><span>
</span><span><span class="hljs-attr">local_infile</span></span><span>=</span><span><span class="hljs-number">1</span></span><span>
</span></span>
PHP配置中启用了 mysqli.allow_local_infile:
查看 php.ini 是否包含以下内容:
<span><span><span class="hljs-attr">mysqli.allow_local_infile</span></span><span> = </span><span><span class="hljs-literal">On</span></span><span>
</span></span>
连接数据库时,添加 MYSQLI_CLIENT_LOCAL_FILES 选项:
<span><span><span class="hljs-variable">$mysqli</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mysqli_init</span></span><span>();
</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">options</span></span><span>(MYSQLI_OPT_LOCAL_INFILE, </span><span><span class="hljs-literal">true</span></span><span>);
</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">real_connect</span></span><span>(</span><span><span class="hljs-string">"host"</span></span><span>, </span><span><span class="hljs-string">"user"</span></span><span>, </span><span><span class="hljs-string">"password"</span></span><span>, </span><span><span class="hljs-string">"database"</span></span><span>, </span><span><span class="hljs-literal">null</span></span><span>, </span><span><span class="hljs-literal">null</span></span><span>, MYSQLI_CLIENT_LOCAL_FILES);
</span></span>
该函数的作用是将本地文件内容通过缓冲的方式提供给MySQL。下面是一个读取CSV文件的示例:
<span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">localInfileReader</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-variable">$stream</span></span></span><span>, &</span><span><span class="hljs-variable">$buffer</span></span><span>, </span><span><span class="hljs-variable">$buflen</span></span><span>)
{
</span><span><span class="hljs-built_in">static</span></span><span> </span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$fp</span></span><span>) {
</span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fopen</span></span><span>(</span><span><span class="hljs-string">'/path/to/data.csv'</span></span><span>, </span><span><span class="hljs-string">'r'</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$fp</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> -</span><span><span class="hljs-number">1</span></span><span>;
}
}
</span><span><span class="hljs-variable">$data</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fread</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>, </span><span><span class="hljs-variable">$buflen</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$data</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> -</span><span><span class="hljs-number">1</span></span><span>;
}
</span><span><span class="hljs-variable">$buffer</span></span><span> = </span><span><span class="hljs-variable">$data</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">feof</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>)) {
</span><span><span class="hljs-title function_ invoke__">fclose</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>);
</span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
}
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>);
}
</span></span>
以下是一个完整的示例,展示了如何使用 set_local_infile_handler() 实现本地文件数据的导入:
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$mysqli</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mysqli_init</span></span><span>();
</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">options</span></span><span>(MYSQLI_OPT_LOCAL_INFILE, </span><span><span class="hljs-literal">true</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">real_connect</span></span><span>(</span><span><span class="hljs-string">'localhost'</span></span><span>, </span><span><span class="hljs-string">'root'</span></span><span>, </span><span><span class="hljs-string">'password'</span></span><span>, </span><span><span class="hljs-string">'test_db'</span></span><span>, </span><span><span class="hljs-literal">null</span></span><span>, </span><span><span class="hljs-literal">null</span></span><span>, MYSQLI_CLIENT_LOCAL_FILES)) {
</span><span><span class="hljs-keyword">die</span></span><span>(</span><span><span class="hljs-string">'Connect Error ('</span></span><span> . </span><span><span class="hljs-title function_ invoke__">mysqli_connect_errno</span></span><span>() . </span><span><span class="hljs-string">') '</span></span><span> . </span><span><span class="hljs-title function_ invoke__">mysqli_connect_error</span></span><span>());
}
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">localInfileReader</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-variable">$stream</span></span></span><span>, &</span><span><span class="hljs-variable">$buffer</span></span><span>, </span><span><span class="hljs-variable">$buflen</span></span><span>)
{
</span><span><span class="hljs-built_in">static</span></span><span> </span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$fp</span></span><span>) {
</span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fopen</span></span><span>(</span><span><span class="hljs-string">'/path/to/data.csv'</span></span><span>, </span><span><span class="hljs-string">'r'</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$fp</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> -</span><span><span class="hljs-number">1</span></span><span>;
}
}
</span><span><span class="hljs-variable">$data</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fread</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>, </span><span><span class="hljs-variable">$buflen</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$data</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> -</span><span><span class="hljs-number">1</span></span><span>;
}
</span><span><span class="hljs-variable">$buffer</span></span><span> = </span><span><span class="hljs-variable">$data</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">feof</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>)) {
</span><span><span class="hljs-title function_ invoke__">fclose</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>);
</span><span><span class="hljs-variable">$fp</span></span><span> = </span><span><span class="hljs-literal">null</span></span><span>;
}
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>);
}
</span><span><span class="hljs-comment">// 设置读取处理器</span></span><span>
</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">set_local_infile_handler</span></span><span>(</span><span><span class="hljs-string">"localInfileReader"</span></span><span>);
</span><span><span class="hljs-comment">// 执行导入语句</span></span><span>
</span><span><span class="hljs-variable">$sql</span></span><span> = </span><span><span class="hljs-string">"LOAD DATA LOCAL INFILE 'dummy.csv' INTO TABLE my_table FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\n'"</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">query</span></span><span>(</span><span><span class="hljs-variable">$sql</span></span><span>)) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"导入失败: ("</span></span><span> . </span><span><span class="hljs-variable">$mysqli</span></span><span>->errno . </span><span><span class="hljs-string">") "</span></span><span> . </span><span><span class="hljs-variable">$mysqli</span></span><span>->error;
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"导入成功!"</span></span><span>;
}
</span><span><span class="hljs-comment">// 关闭处理器</span></span><span>
</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">set_local_infile_default</span></span><span>();
</span><span><span class="hljs-variable">$mysqli</span></span><span>-></span><span><span class="hljs-title function_ invoke__">close</span></span><span>();
</span><span><span class="hljs-meta">?></span></span><span>
</span></span>
说明:
dummy.csv 是一个占位文件名,MySQL不会实际读取该路径,而是依赖你提供的回调函数。
回调函数内的真实文件路径可以是任意合法的可读路径。
回调函数必须能持续读取并正确返回数据长度。
一定要关闭回调(set_local_infile_default()),避免后续操作中误触发。
使用 LOAD DATA LOCAL INFILE 前请确保数据库用户有足够权限。
通过 set_local_infile_handler() 方法,PHP提供了对 LOAD DATA LOCAL INFILE 更加安全和可控的支持方式。这种方法尤其适用于对数据输入有严格验证需求的场景。虽然使用略显复杂,但在实际生产环境中,它为开发者提供了宝贵的灵活性和安全保障。
掌握这一技巧,将为你处理大规模数据导入任务增添有力工具。
相关标签:
MySQL