You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用express-fileupload向SQL Server上传图片失败问题求助

问题描述

我用 Node.js、Express、express-fileupload 和 SQL Server 开发签名图片上传至数据库的功能,执行代码时遇到如下错误:

Cannot bulk load because the file "\tmp\tmp-1-1686629760086" could not be opened

我的存储过程

CREATE PROCEDURE Insert_Signatures_SP
    (@SignatureUri NVARCHAR(1000),
     @UserID INT)
AS
    DELETE FROM Signatures
    WHERE UserID_FK = @UserID;

    DECLARE @err INT
    DECLARE @sql NVARCHAR(MAX)

    SET @sql = N'INSERT INTO Signatures (UserID_FK, [Signature])

    SELECT @UserID, BulkColumn
    FROM OPENROWSET
    (
        Bulk '''+ @SignatureUri +''', SINGLE_BLOB
    ) AS [Signature]';

    EXEC @err = sp_executesql @sql, N'@UserID INT', @UserID; 
    RETURN(@err);
GO

EJS 文件

<form action="/admin-signatures-upload" method="post" enctype="multipart/form-data">
  <input type="hidden" class="hiddenField" id="userid" name="userid" value="<%= user.UserID %>" required>
  <button id="<%= user.UserID %>" class="btn1 catbtn" >Upload Signature</button>
  <div>
    <input type="file" name="myImage">
  </div>
</form>

server.js 文件

const fileUpload = require('express-fileupload');

app.use(fileUpload({
    useTempFiles : true,
    tempFileDir : '/tmp/'
}));

app.post("/admin-signatures-upload", async function (req, res) {
    try {

        if (!req.files) {
            return res.status(400).send("No files were uploaded.");
        }

        console.log('Path = ' + file.tempFilePath);

        await pool.connect();

        const resultUserCategories = await pool.request()
            .input('SignatureUri', file.tempFilePath)
            .input('UserID', req.body.id)
            .execute(`Insert_Signatures_SP`);

        await pool.close();

        res.redirect('/admin-signatures');

    } catch (error) {
    }
})

解决方案

核心问题

OPENROWSET(BULK ...) 是在SQL Server 服务器端执行的,它读取的文件路径必须是服务器能访问到的路径。你现在传的是 Node.js 服务所在机器的 /tmp 临时文件路径——如果 SQL Server 和 Node.js 不在同一台机器,服务器根本找不到这个文件;就算在同一台机器,SQL Server 服务账号也大概率没有 /tmp 目录的读取权限。

另外你的代码还有几个小bug:

  • server.js 里未定义 file 变量,应该从 req.files 中获取上传的文件
  • 表单隐藏字段名是 userid,但代码里用 req.body.id,参数名不匹配
  • 每次请求都调用 pool.connect() 和 pool.close() 不合理,连接池应该复用,无需每次关闭

推荐修复方案(直接传递二进制数据)

绕开文件路径问题,直接把文件的二进制数据传给存储过程:

1. 修改存储过程

去掉 OPENROWSET,直接接受二进制参数插入:

CREATE PROCEDURE Insert_Signatures_SP
    (@Signature VARBINARY(MAX),
     @UserID INT)
AS
    DELETE FROM Signatures
    WHERE UserID_FK = @UserID;

    INSERT INTO Signatures (UserID_FK, [Signature])
    VALUES (@UserID, @Signature);

    RETURN @@ERROR;
GO

2. 修改 server.js

const fileUpload = require('express-fileupload');

app.use(fileUpload({
    useTempFiles : true,
    tempFileDir : '/tmp/'
}));

app.post("/admin-signatures-upload", async function (req, res) {
    try {
        if (!req.files || !req.files.myImage) {
            return res.status(400).send("No files were uploaded.");
        }

        const file = req.files.myImage;
        const userId = req.body.userid; // 对应表单的name="userid"

        // 连接池无需每次connect,直接用request即可
        await pool.request()
            .input('Signature', file.data) // 直接传递文件二进制Buffer
            .input('UserID', userId)
            .execute('Insert_Signatures_SP');

        res.redirect('/admin-signatures');
    } catch (error) {
        console.error(error);
        res.status(500).send("Upload failed");
    }
})

备选方案(让SQL Server访问临时文件,不推荐)

如果一定要用 OPENROWSET,需要满足以下条件:

  1. SQL Server 和 Node.js 部署在同一台机器,或者将临时文件放在双方都能访问的共享目录
  2. 给 SQL Server 服务账号分配 /tmp 目录的读取权限
  3. 传递完整的绝对路径(比如 Linux 下的 /tmp/tmp-1-1686629760086,Windows 下的 C:\tmp\...)
  4. 修复 server.js 里的变量和参数名错误:
    const file = req.files.myImage;
    console.log('Path = ' + file.tempFilePath);
    // ...
    .input('UserID', req.body.userid)
    

内容的提问来源于stack exchange,提问作者kbdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 19:05:06