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

Ubuntu服务器使用LibreOffice转换DOCX到PDF时出现CalledProcessError错误

DOCX转PDF触发CalledProcessError(退出码77)的解决方案

问题现象

在Ubuntu服务器上通过HTML表单上传DOCX文件后,调用LibreOffice转换为PDF时,/add-document路径下触发CalledProcessError,执行命令返回非零退出状态码77。执行的错误命令如下:

['libreoffice', '--headless', '--convert-to', 'pdf', '/var/www/html/dev/media/documents/Lavender_Bay_Boatshed_Website_Testing_61gWm6r.docx', '--outdir', '/var/www/html/dev/media/documents/Lavender Bay Boatshed Website Testing.pdf']

对应Django视图代码

@login_required
def add_document(request):
    title = "Add document"
    if request.method == 'POST':
        file = request.FILES.get('file')
        check = request.POST.get('check')
        
        subject = request.POST.get('subject')
        message = request.POST.get('message')
        print(request.POST)

        names = []
        emails = []
        for key in request.POST:
            
            if key.startswith('email_'):
                emails.append(request.POST[key])
            elif key.startswith('name_'):
                names.append(request.POST[key])
        print(emails)
        print(names)
        recipients = []
        list_of_dicts = [{'email': email, 'name': name} for email, name in zip(emails, names)]
        if file:
            if (file.name.endswith('.jpg')) or (file.name.endswith('.png')):
                obj = Documents(document=file,name=file.name,user=request.user)
                obj.save()
                image = Image.open(obj.document.path)
                iml = image.convert('RGB')
                folder = r'C:\Users\admin\Desktop\projects\Document-sign\media\documents'
                path = os.path.join(folder, file.name.split('.')[0] + '.pdf')
                os.remove(obj.document.path)
                iml.save(path)
                obj.document = f"documents/{file.name.split('.')[0]}.pdf"
                obj.save()
                os.remove(obj.document.path)
                if check:
                    request.session['check'] = False
                    return redirect('sign_document')
                else:
                    recipients.append({'emails':list_of_dicts,'subject':subject,'message':message})
                    request.session['recipients'] = recipients
                    request.session['check'] = True
                    return redirect('make_envelope')
                
            elif file.name.endswith('.docx'):
                obj = Documents(document=file, name=file.name, user=request.user)
                obj.save()

                # Define paths
                path = os.path.join(settings.MEDIA_ROOT, 'documents')
                input_path = obj.document.path
                

                # Run LibreOffice conversion
                try:
                    result = subprocess.run(
                        ['libreoffice', '--headless', '--convert-to', 'pdf', input_path, '--outdir', path],
                        check=True,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE
                    )
                    print(result.stdout)
                    print(result.stderr)
                except subprocess.CalledProcessError as e:
                    print("Error occurred:", e.stderr)
                    raise

                # Remove the original DOCX file
                os.remove(input_path)

                # Update the document path to the converted PDF
                obj.document = f"documents/{file.name.split('.')[0]}.pdf"
                obj.save()
                
                if check:
                    request.session['check'] = False
                    return redirect('sign_document')
                else:
                    recipients.append({'emails':list_of_dicts,'subject':subject,'message':message})
                    request.session['recipients'] = recipients
                    request.session['check'] = True
                    return redirect('make_envelope')
            else:
                obj = Documents(document=file,name=file.name,user=request.user)
                obj.save()
                if check:
                    
                    request.session['check'] = False
                    return redirect('sign_document')
                else:
                    recipients.append({'emails':list_of_dicts,'subject':subject,'message':message})
                    print(recipients)
                    request.session['recipients'] = recipients
                    request.session['check'] = True
                    return redirect('make_envelope')   
            
    context = {'title':title}
    return render(request, 'myapp/add_document.html',context=context)

解决步骤

  • 修复--outdir参数错误:错误命令中--outdir后跟随的是PDF文件名而非目录,需确保该参数指向存储PDF的目录(代码中path变量已为正确目录,需排查是否有其他逻辑篡改了该值)。
  • 安装无头模式依赖:退出码77通常是LibreOffice缺少显示依赖导致,执行以下命令安装必要组件:
    sudo apt-get install libreoffice libreoffice-writer xvfb
    
    修改转换命令,通过xvfb-run运行LibreOffice以避免无头模式显示问题:
    result = subprocess.run(
        ['xvfb-run', '--auto-servernum', 'libreoffice', '--headless', '--convert-to', 'pdf', input_path, '--outdir', path],
        check=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    
  • 修正文件权限:确保Django运行用户(通常为www-data)对媒体目录有读写权限:
    sudo chown -R www-data:www-data /var/www/html/dev/media/documents
    sudo chmod -R 755 /var/www/html/dev/media/documents
    
  • 路径转义处理:对含空格的文件路径进行转义,避免命令解析错误:
    import shlex
    result = subprocess.run(
        ['xvfb-run', '--auto-servernum', 'libreoffice', '--headless', '--convert-to', 'pdf', shlex.quote(input_path), '--outdir', shlex.quote(path)],
        check=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    
  • 清理残留进程:LibreOffice无头模式可能残留进程导致转换失败,可在转换前清理:
    subprocess.run(['pkill', '-f', 'libreoffice'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 22:07:25