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

创建带签名的新用户时签名文件总是损坏(0KB无法打开)

问题:签名文件生成后为0KB且无法打开

每次创建带签名的新用户时,生成的签名文件大小为0KB,无法打开;文件路径可正常访问,但文件损坏无法显示。此前功能正常运行,现在出现异常。已完成storage/app/public/signatures/的符号链接配置,文件能被创建但始终损坏。


相关代码

Laravel控制器(存储用户与签名)

public function store(Request $request)
{
    // Save Signature from data URL to storage
    $signatureData = $request->signature;
    $fileName = time() . '.png';
    $filePath = 'public/signatures/' . $fileName;  // Save to storage/app/public
    $imageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $signatureData));
    Storage::put($filePath, $imageData);

    // Validasi dan penyimpanan data user
    $user = User::create([
        'name' => $request->name,
        'user_code' => $request->user_code,
        'password' => Hash::make($request->password),
        'signature_path' => 'signatures/' . $fileName,  // Save part of path todatabase
    ]);

    // Add role(Jabatans) to user with pivot table
    $user->jabatans()->attach($request->jabatan_id);
    Session::flash('success', 'User Berhasil Dibuat');
    return redirect()->route('user-create');
}

前端签名Canvas代码

<div class="mb-3">
    <label for="signature">Signature</label>
    <canvas id="signature-pad" class="signature-pad" width=400 height=200 style="border:1px solid black;"></canvas>
    <button type="button" id="clearSignature" class="btn btn-secondary mt-2">Clear Signature</button>
</div>

签名Pad相关JavaScript代码

<script>
    var canvas = document.getElementById('signature-pad');
    var signaturePad = new SignaturePad(canvas);

    // Function to check if the form is completely filled out
    function isFormValid(formId) {
        const form = document.getElementById(formId);
        return form.checkValidity();
    }

    // Function to display SweetAlert confirmation
    function showConfirmationAlert() {
        if (isFormValid('userForm')) { 
            // Replace 'userForm' with your form's ID
            Swal.fire({
                title: 'Are you sure?',
                text: 'Do you want to submit this form?',
                icon: 'warning',
                showCancelButton: true,
                confirmButtonColor: '#3085d6',
                cancelButtonColor: '#d33',
                confirmButtonText: 'Yes'
            }).then((result) => {
                if (result.isConfirmed) {
                    // Submit the form
                    document.getElementById('userForm').submit(); 
                    // Replace 'userForm' with your form's ID

                    // Show success alert
                    Swal.fire(
                        'Success!',
                        'Your form has been submitted.',
                        'success'
                    );
                }
            });
        } else {
            Swal.fire(
                'Error!',
                'Please fill out all required fields.',
                'error'
            );
        }
    }

    // Add an event listener to the clear signature button
    document.getElementById('clearSignature').addEventListener('click', function() {
        signaturePad.clear();
    });

    // Add an event listener to the submit button
    document.getElementById('submitButton').addEventListener('click', function(e) {
        e.preventDefault();

        // Check the signature first
        if (signaturePad.isEmpty()) {
            Swal.fire({
                icon: 'error',
                title: 'Oops...',
                text: 'Please provide your signature.',
            });
            return;
        }

        // Then, check for user code duplication
        const userCode = document.getElementById('user_code').value;

        fetch('/check-usercode', {
                method: 'POST',
                body: JSON.stringify({
                    user_code: userCode
                }),
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': '{{ csrf_token() }}'
                }
            })
            .then(response => response.json())
            .then(data => {
                if (data.exists) {
                    // If the user code already exists, show a warning with SweetAlert
                    Swal.fire({
                        icon: 'error',
                        title: 'Oops...',
                        text: 'User code already exists!',
                    });
                } else {
                    // If the user code is unique, display a form submission confirmation
                    showConfirmationAlert();
                }
            })
            .catch(error => console.error('Error:', error));
    });
</script>

排查与修复方案

1. 核心问题:签名数据未提交到后端

当前代码中没有将Canvas的签名数据作为表单字段提交。前端仅初始化了SignaturePad,但表单提交时,签名的base64数据并未传入后端,导致$request->signature为空,解码后写入空文件(0KB)。

修复步骤:

  • 在表单内部添加隐藏输入框,用于存储签名数据:
<form id="userForm">
    <!-- 其他表单字段 -->
    <input type="hidden" name="signature" id="signature-input">
    <!-- 提交按钮等 -->
</form>
  • 修改showConfirmationAlert函数,在提交表单前将签名数据存入隐藏输入框:
function showConfirmationAlert() {
    if (isFormValid('userForm')) { 
        // 新增:将签名数据存入隐藏字段
        document.getElementById('signature-input').value = signaturePad.toDataURL();
        
        Swal.fire({
            title: 'Are you sure?',
            text: 'Do you want to submit this form?',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes'
        }).then((result) => {
            if (result.isConfirmed) {
                document.getElementById('userForm').submit(); 
                Swal.fire(
                    'Success!',
                    'Your form has been submitted.',
                    'success'
                );
            }
        });
    } else {
        Swal.fire(
            'Error!',
            'Please fill out all required fields.',
            'error'
        );
    }
}

2. 优化后端解码逻辑,增加错误处理

避免空数据或无效数据写入文件,在控制器中添加校验:

public function store(Request $request)
{
    // 新增:校验签名数据是否存在
    $request->validate([
        'signature' => 'required|string',
    ]);

    $signatureData = $request->signature;
    $fileName = time() . '.png';
    $filePath = 'public/signatures/' . $fileName;

    // 优化解码逻辑,添加错误检查
    $imageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $signatureData), true);
    if ($imageData === false) {
        return back()->withErrors(['signature' => '签名数据无效,请重新签名']);
    }

    Storage::put($filePath, $imageData);

    // 剩余代码保持不变...
}

3. 验证存储目录权限

确保存储目录有正确的写入权限:

# 赋予存储目录读写权限
chmod -R 755 storage/
# 调整目录所属用户(根据服务器环境,比如Apache用www-data,Nginx用nginx)
chown -R www-data:www-data storage/

4. 调试数据传输过程

  • 前端调试:在showConfirmationAlert中打印签名数据,确认格式正确:
console.log(signaturePad.toDataURL());
// 应输出类似:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
  • 后端调试:在控制器中打印接收到的签名数据,确认非空:
dd($request->signature);
// 应输出完整的data URL字符串

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:27:11