【问题标题】:Flutter web - display pdf file generated in application. (Uint8List format)Flutter web - 显示应用程序中生成的 pdf 文件。 (Uint8List 格式)
【发布时间】:2020-08-05 23:34:52
【问题描述】:

我正在开发一个 iOS/Android/Web 应用程序,我正在使用此处的 pdf 插件根据用户输入生成 PDF:https://pub.dev/packages/pdf。在 iOS 和 Android 上,我可以访问文件系统,所以我可以先保存生成的 pdf,然后使用任何 pdf 显示插件打开它。

但是,据我所知,对于 Flutter web,无法访问任何类型的文件系统。我见过的所有 pdf 渲染插件都假定 pdf 文件要么存储在文件中,要么存储在网络上。 我看了看:

  • flutter_full_pdf_viewer: ^1.0.6
  • flutter_pdf_viewer:^0.6.1
  • pdf_render: ^0.57.2

如何在 Flutter Web 上显示这个应用生成的 pdf?有没有办法将 Uint8List 数据欺骗为文件,或者可能有另一种方法来解决这个问题? (我能想到的解决这个问题的唯一方法是在生成文件后将其上传到网络以显示它,但这似乎有点矫枉过正。)

【问题讨论】:

    标签: file pdf flutter flutter-web uint8list


    【解决方案1】:

    我们不需要额外的插件就可以做到,因为网络浏览器本身可以显示(或下载)pdf 文档。

    为 pdf 2.0.0 及更新版本更新

    由于方法save 现在返回Future,按钮处理程序变为异步(当然您可以使用FurureBuilder 或其他方式)。

    import 'package:flutter/material.dart';
    import 'package:pdf/pdf.dart';
    import 'package:pdf/widgets.dart' as pw;
    import 'package:universal_html/html.dart' as html;
    
    class PdfLabPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final pdf = pw.Document();
        pdf.addPage(pw.Page(
            pageFormat: PdfPageFormat.a4,
            build: (pw.Context context) {
              return pw.Center(
                child: pw.Text('Hello World'),
              );
            }));
    
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ElevatedButton(
                  onPressed: () async {
                    final bytes = await pdf.save();
                    final blob = html.Blob([bytes], 'application/pdf');
                    final url = html.Url.createObjectUrlFromBlob(blob);
                    html.window.open(url, '_blank');
                    html.Url.revokeObjectUrl(url);
                  },
                  child: Text('Open'),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final bytes = await pdf.save();
                    final blob = html.Blob([bytes], 'application/pdf');
                    final url = html.Url.createObjectUrlFromBlob(blob);
                    final anchor = html.AnchorElement()
                      ..href = url
                      ..style.display = 'none'
                      ..download = 'some_name.pdf';
                    html.document.body?.children.add(anchor);
                    anchor.click();
                    html.document.body?.children.remove(anchor);
                    html.Url.revokeObjectUrl(url);
                  },
                  child: Text('Download'),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    原答案

    import 'package:flutter/material.dart';
    import 'package:pdf/pdf.dart';
    import 'package:pdf/widgets.dart' as pw;
    import 'package:universal_html/html.dart' as html;
    
    class PdfDemo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final pdf = pw.Document();
        pdf.addPage(pw.Page(
            pageFormat: PdfPageFormat.a4,
            build: (pw.Context context) {
              return pw.Center(
                child: pw.Text("Hello World"),
              );
            }));
        final bytes = pdf.save();
        final blob = html.Blob([bytes], 'application/pdf');
    
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("Open"),
                  onPressed: () {
                    final url = html.Url.createObjectUrlFromBlob(blob);
                    html.window.open(url, "_blank");
                    html.Url.revokeObjectUrl(url);
                  },
                ),
                RaisedButton(
                  child: Text("Download"),
                  onPressed: () {
                    final url = html.Url.createObjectUrlFromBlob(blob);
                    final anchor =
                        html.document.createElement('a') as html.AnchorElement
                          ..href = url
                          ..style.display = 'none'
                          ..download = 'some_name.pdf';
                    html.document.body.children.add(anchor);
                    anchor.click();
                    html.document.body.children.remove(anchor);
                    html.Url.revokeObjectUrl(url);
                  },
                ),
              ],
              mainAxisAlignment: MainAxisAlignment.center,
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 非常感谢您编写此代码。我一直在努力克服这个问题,但我从未想过使用 html。太棒了!
    • 我真的不熟悉HTML,有没有办法在浏览器中打开PDF而不是下载时设置PDF文件的名称?现在它被设置为一些随机字符串,如“006f36b8-08b5-4530-9e7d-30a7a67e6d1e”。另外,非常感谢您提供此解决方案!效果很好。
    • 对于那些收到“加载 PDF 文档失败”的人,请确保您传递了正确的 blob。例如最终 url = html.Url.createObjectUrlFromBlob(html.Blob([pdfData.buffer]));
    • 最终字节 = 等待 pdf.save();您需要添加 await pdf save 以避免损坏的 pdf 和不加载等
    • 谢谢@iluvatar_GR,我已经更新了答案。
    【解决方案2】:

    2021 年 2 月 12 日更新:

    经过多轮谷歌搜索和深入研究文档,我已经开始工作了。以下是要点:

    1- 无需像我之前指出的那样将任何 javascript 代码插入到 &lt;app_directory&gt;/web/index.html 文件中。对不起!我正在学习颤振/飞镖...

    2- 我必须创建一个 Future&lt;html.Blob&gt; 方法(请查看 myGetBlobPdfContent() async {...} )来处理 html/pdf 内容、保存 (await pdf.save()) 并返回 Blob 值。我不适合说它与“不完整的未来”有关,在 pdf 的东西上扔了一个空点......

    3- 此外,那些 RaisedButton 的 onPressed 方法已更改为异步,以允许将 pdf 内容作为 Blob 值 (createObjectUrlFromBlob( await myGetBlobPdfContent());)。

    4- 最后,我收到了警告消息:Helvetica has no Unicode support see https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management,因此我安装了自定义字体,如 this cookbook instructions,在此示例中,我设置了 Arial ttf (data = await rootBundle.load("fonts/arial.ttf");),这些警告消息消失了。

    5- 这是我的pubspec.yaml 的sn-p:

    ...
    dependencies:
      flutter:
        sdk: flutter
      pdf: ^2.1.0
      universal_html: ^1.2.4
    
    ...
    
    fonts:
      - family: Arial
        fonts:
          - asset: fonts/arial.ttf
          - asset: fonts/arialbd.ttf
            weight: 700
          - asset: fonts/ariali.ttf
            style: italic
      ...
    

    6- 还有一个最小的工作示例(完整的main.dart 文件):

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:pdf/pdf.dart';
    import 'package:pdf/widgets.dart' as pw;
    import 'package:universal_html/html.dart' as html;
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: PdfDemo(),
        );
      }
    }
    
    class PdfDemo extends StatelessWidget {
      Future<html.Blob> myGetBlobPdfContent() async {
        var data = await rootBundle.load("fonts/arial.ttf");
        var myFont = pw.Font.ttf(data);
        var myStyle = pw.TextStyle(font: myFont, fontSize: 36.0);
    
        final pdf = pw.Document();
        pdf.addPage(pw.Page(
            pageFormat: PdfPageFormat.a4,
            build: (pw.Context context) {
              return pw.Center(
                child: pw.Text(
                  "Hello World",
                  style: myStyle,
                ),
                // child: pw.Text("Hello World", style: myStyle),
              );
            }));
        final bytes = await pdf.save();
        html.Blob blob = html.Blob([bytes], 'application/pdf');
        return blob;
      }
    
      @override
      Widget build(BuildContext context) {
        myGetBlobPdfContent();
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("Open"),
                  onPressed: () async {
                    final url = html.Url.createObjectUrlFromBlob(
                        await myGetBlobPdfContent());
                    html.window.open(url, "_blank");
                    html.Url.revokeObjectUrl(url);
                  },
                ),
                RaisedButton(
                  child: Text("Download"),
                  onPressed: () async {
                    final url = html.Url.createObjectUrlFromBlob(
                        await myGetBlobPdfContent());
                    final anchor =
                        html.document.createElement('a') as html.AnchorElement
                          ..href = url
                          ..style.display = 'none'
                          ..download = 'some_name.pdf';
                    html.document.body.children.add(anchor);
                    anchor.click();
                    html.document.body.children.remove(anchor);
                    html.Url.revokeObjectUrl(url);
                  },
                ),
              ],
              mainAxisAlignment: MainAxisAlignment.center,
            ),
          ),
        );
      }
    }
    

    7- 最后,PDF 生成(和下载)按预期工作:

    希望这会有所帮助。

    初次发帖(仅保留信息日志)

    @Spatz 的回答可能会解决网络平台的任何 pdf 问题。我不知道为什么它没有按预期工作,至少对于我的设置(见下文)。

    我的意思是网页尝试打开pdf并抛出错误:Failed to load PDF document.

    此外,该示例成功下载了一个 pdf 文件 (some_name.pdf),但是当我尝试打开该 pdf 文件 (error: file corrupted) 时它失败了。

    我看到this post 谈到将javascript 代码插入/web/index.html 文件。我已经插入了这些行,也没有成功。

    我使用的源代码是一个main.dart文件:

    import 'package:flutter/material.dart';
    import 'package:pdf/pdf.dart';
    import 'package:pdf/widgets.dart' as pw;
    import 'package:universal_html/html.dart' as html;
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: PdfDemo(),
        );
      }
    }
    
    class PdfDemo extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final pdf = pw.Document();
        pdf.addPage(pw.Page(
            pageFormat: PdfPageFormat.a4,
            build: (pw.Context context) {
              return pw.Center(
                child: pw.Text("Hello World"),
              );
            }));
        final bytes = pdf.save();
        final blob = html.Blob([bytes], 'application/pdf');
    
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("Open"),
                  onPressed: () {
                    final url = html.Url.createObjectUrlFromBlob(blob);
                    html.window.open(url, "_blank");
                    html.Url.revokeObjectUrl(url);
                  },
                ),
                RaisedButton(
                  child: Text("Download"),
                  onPressed: () {
                    final url = html.Url.createObjectUrlFromBlob(blob);
                    final anchor =
                        html.document.createElement('a') as html.AnchorElement
                          ..href = url
                          ..style.display = 'none'
                          ..download = 'some_name.pdf';
                    html.document.body.children.add(anchor);
                    anchor.click();
                    html.document.body.children.remove(anchor);
                    html.Url.revokeObjectUrl(url);
                  },
                ),
              ],
              mainAxisAlignment: MainAxisAlignment.center,
            ),
          ),
        );
      }
    }
    

    我的设置是:

    • Flutter(频道测试版,1.26.0-17.3.pre,在 Microsoft Windows [版本 10.0.19042.746], 语言环境 en-150)
    • Android工具链为Android设备开发(Android SDK版本 30.0.3)
    • Chrome - 为网络开发
    • Android Studio(4.1.0 版)

    谢谢你:)

    【讨论】:

      【解决方案3】:

      为了解决“无法打开 PDF 文件”...这是由于文件格式无效...在现有代码中添加以下两行:

       var  data = await pdf.save();
       Uint8List bytes = Uint8List.fromList(data);
      

      完整的代码如下:

        var  data = await pdf.save();
          Uint8List bytes = Uint8List.fromList(data);
          final blob = html.Blob([bytes], 'application/pdf');
          final url = html.Url.createObjectUrlFromBlob(blob);
          final anchor =
          html.document.createElement('a') as html.AnchorElement
            ..href = url
            ..style.display = 'none'
            ..download = 'some_name.pdf';
      
          html.document.body.children.add(anchor);
          anchor.click();
          html.document.body.children.remove(anchor);
          html.Url.revokeObjectUrl(url);
      

      【讨论】:

      • 太棒了!它完美地工作。谢谢@Haider Ali
      猜你喜欢
      • 2020-12-08
      • 1970-01-01
      • 2016-11-26
      • 2020-04-14
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 2022-08-18
      • 2022-01-11
      相关资源
      最近更新 更多