Laravel 9运行Python脚本/EXE文件报无法创建临时目录错误
问题描述
在Laravel 9应用开发过程中,先后尝试通过exec()、shell_exec()函数、Symfony Process组件运行Python脚本及打包生成的exe可执行文件,所有方式均执行失败。调试时返回INTERNAL ERROR: cannot create temporary directory!错误,将可执行文件路径从原路径C:/Users/edwin/Documents/lectordocs/lectorDocs.exe更换为项目路径C:\xampp\htdocs\Login\login-laravel\public\assets\python后,相同报错仍然出现。
涉及代码
Python脚本
#!/usr/bin/env python from re import L import sys import MySQLdb miConexion = MySQLdb.connect( host='127.0.0.1', user= 'root', db='pruebas' ) cur = miConexion.cursor() mySqlInsert = "INSERT INTO documentFin (co, localizacion, categoria, idItem, nombre, cantidad, fecha, lote) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)" def insertarTabla(co, localizacion, categoria, idItem, nombre, cantidad, fecha, lote): val = (co, localizacion, categoria, idItem, nombre, cantidad, fecha, lote) try: cur.execute(mySqlInsert, val) miConexion.commit() except: print(val) def verificarFecha(fecha): if(len(fecha) == 10): if(fecha[4] == "/" and fecha[7] == "/"): return True else: return False else: return False def probar(): with open("UCIN3053.p01") as archivo: lines = archivo.readlines() co = "" localizacion = "" categoria = "" contadorMas = 0 matriz = [] listaTemp = [] for x in range(len(lines)): temp = lines[x].split() if(len(temp) > 1): if(temp[1] == "C.O."): co = temp[3] + " " + temp[4] elif(temp[1] == "Localizacion"): local = temp[3:len(temp) - 1] elif(temp[1] == "Categoria"): categoria = temp[3] if(temp[0].isnumeric()): if(temp[1].isnumeric()): listaTemp.append(cod) listaTemp.append(cadena) listaTemp.append(temp[1]) listaTemp.append(temp[0]) listaTemp.append("") matriz.append(listaTemp) listaTemp = [] elif(verificarFecha(temp[1])): listaTemp.append(cod) listaTemp.append(cadena) listaTemp.append(temp[2]) listaTemp.append(temp[0]) listaTemp.append(temp[1]) matriz.append(listaTemp) listaTemp = [] else: listaTemp.append(temp[0]) cod = temp[0] cadena = "" for x in range(1, len(temp)): if(temp[x].isnumeric() and (temp[x + 1] == "UND" or temp[x + 1] == "FCO" or temp[x + 1] == "CJA" or temp[x + 1] == "FRA")): numTemp = x break cadena += temp[x] + " " listaTemp.append(cadena) listaTemp.append(temp[numTemp]) listaTemp.append("") listaTemp.append("") matriz.append(listaTemp) listaTemp = [] elif(temp[0] != "|"): if(temp[0][len(temp[0]) - 2]) == "-": if(verificarFecha(temp[1])): listaTemp.append(cod) listaTemp.append(cadena) listaTemp.append(temp[2]) listaTemp.append(temp[0]) listaTemp.append(temp[1]) matriz.append(listaTemp) listaTemp = [] else: listaTemp.append(cod) listaTemp.append(cadena) listaTemp.append(temp[1]) listaTemp.append(temp[0]) listaTemp.append("") matriz.append(listaTemp) listaTemp = [] elif(len(temp) == 1): contadorMas += 1 if(contadorMas == 4): contadorMas = 0 for w in range(len(local)): localizacion += local[w] + " " #Añadir matriz e identificadores for x in range(len(matriz)): insertarTabla(co, localizacion, categoria, matriz[x][0], matriz[x][1], matriz[x][2], matriz[x][3], matriz[x][4]) matriz = [] localizacion = "" cur.close() probar()
Laravel控制器代码
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; class InventarioController extends Controller { public function reportar(Request $request){ $file = 'C:/Users/edwin/Documents/lectordocs/lectorDocs.exe'; if(file_exists($file)){ // echo 'File does exist :: ' . $file; $process = new Process([$file]); $process->run(); // executes after the command finishes if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } return redirect()->to("/login"); } else{ return redirect()->to("/home"); } } }
报错原因
这个错误是PyInstaller单文件(onefile)模式打包的exe的典型运行错误:单文件模式打包的exe启动时,会默认向系统临时目录写入解压后的Python运行时、依赖库文件,创建_MEIxxxxxx格式的临时文件夹。如果PHP运行宿主(XAMPP环境下一般是Apache服务)的启动身份对系统临时目录没有写入权限、系统盘剩余空间不足,就会抛出该错误,和exe本身存放路径没有直接关系。
另外现有代码还存在工作目录不匹配的隐患:Python代码用相对路径读取UCIN3053.p01文件,Symfony Process默认继承PHP进程的工作目录,不是exe所在目录,即使临时目录问题修复,也会出现找不到文件的错误。
解决方案
按优先级依次排查处理:
- 调整临时目录权限:找到系统临时目录(默认路径为
C:\Windows\TEMP、C:\Users\<当前系统用户>\AppData\Local\Temp),给Apache服务的运行用户添加读取、写入、修改权限。测试阶段可以临时把Apache服务的登录身份改为本地管理员账户,确认问题根因是权限后再收敛到最小必要权限,不要长期用管理员权限运行Web服务。 - 打包时指定自定义临时目录:重新用PyInstaller打包exe时,添加
--runtime-tmpdir参数指定一个Web进程有权限写入的目录作为临时解压路径,示例打包命令:
提前手动创建好上述tmp目录,给该目录开放写入权限即可。pyinstaller --onefile --runtime-tmpdir "C:\xampp\htdocs\Login\login-laravel\public\assets\python\tmp" lectorDocs.py - 显式设置Process工作目录:实例化Symfony Process后,调用
setWorkingDirectory方法指定工作目录为exe和UCIN3053.p01文件所在的目录,避免相对路径找不到文件,修改后的代码片段:$process = new Process([$file]); // 替换为你的文件实际存放目录 $process->setWorkingDirectory('C:\xampp\htdocs\Login\login-laravel\public\assets\python'); $process->run(); - 更换打包模式:放弃onefile单文件打包模式,改用onedir模式生成包含所有依赖的文件夹,exe启动时不需要解压临时文件,从根源上规避临时目录创建问题。
- 检查磁盘空间:确认系统盘剩余空间大于1G,磁盘满也会导致临时目录创建失败。
内容的提问来源于stack exchange,提问作者Pipe
相关产品推荐
相关产品推荐

