未处理的异常:PathAccessException:即使在flutter中授予权限也无法打开文件、路径

问题描述 投票:0回答:2

我正在开发一个 flutter 项目,我需要为用户创建一个数字 ID 并保存它的 pdf。因此我使用了“PDF”包。目前我正在创建一个带有文本“Dart is Awesome”的 PDF。但是,当我点击“下载 ID”按钮并选择要保存 pdf 的文件夹位置时,出现错误。 错误是:

E/flutter(31521):[错误:flutter/runtime/dart_vm_initializer.cc(41)]未处理的异常:PathAccessException:无法打开文件,路径='/storage/emulated/0/1Cd/example.pdf'(操作系统错误:权限被拒绝,errno = 13) E/flutter (31521): #0 _checkForErrorResponse (dart:io/common.dart:55:9) E/flutter (31521): #1 _File.open。 (dart:io/file_impl.dart:381:7) E/颤振(31521): E/flutter (31521):#2 DigitalId.build。 (包:rightclaim/screens/digital_id/digital_id.dart:241:9) E/颤振(31521): E/颤动(31521):

我已授予所有权限: 主/AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rightclaim">
   <application
        android:label="rightclaim"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

</manifest>

调试/AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rightclaim">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

配置文件/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rightclaim">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

这是我的 digital_id.dart 代码 您可以跳到 CommonElevatedButton.buttonWithoutIcon(...) 我正在那里做 pdf 工作。

import 'dart:io';

import "package:file_picker/file_picker.dart";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:flutter_svg/svg.dart";
import 'package:path/path.dart' as path;
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import "package:permission_handler/permission_handler.dart";
import "package:right_claim_common/color_constants.dart";
import "package:right_claim_common/common_appbar.dart";
import "package:right_claim_common/common_elevated_buttons.dart";
import "package:right_claim_common/common_textsyles.dart";

import "../../user_data/user_data.dart";
import "userid_details.dart";

class DigitalId extends StatelessWidget {
  UserDigitalId userId = UserDigitalId(
      fullName.toString(),
      "XXXX XXXX XXXX XXXX",
      "12455",
      "9568743367",
      "[email protected]",
      "10/10/2022",
      "Raj Nagar Colony Shahdara Agar,Shahdara",
      "6152 7298 8282",
      "AB+");

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: CommonAppBar(title: "Digital Id"),
        body: Align(
          alignment: Alignment.topCenter,
          child: Padding(
              padding: const EdgeInsets.all(20.0),
              child:
                  Column(mainAxisAlignment: MainAxisAlignment.start, children: [
                Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    borderRadius:
                        const BorderRadius.vertical(top: Radius.circular(10)),
                    color: ColorConstants.primaryColor,
                  ),
                  child: Column(
                    children: [
                      Column(
                        children: [
                          Container(
                              padding: EdgeInsets.all(10),
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.vertical(
                                    top: Radius.circular(10)),
                                color: ColorConstants.tertiaryColor,
                              ),
                            
                              child: Row(
                                children: [
                                  Text(
                                    "Right Claim",
                                    style: CommonTextStyles.text20_700()!
                                        .copyWith(
                                            color: ColorConstants.primaryColor),
                                  ),
                                  Spacer(),
                                  ClipOval(
                                    child: SvgPicture.asset(
                                      'assets/logo2.svg',
                                      width: 50,
                                      height: 50,
                                    ),
                                  ),
                                ],
                              )),
                          Container(
                            padding: EdgeInsets.all(16.0),
                            child: Row(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Expanded(
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        '${userId.name}',
                                        style: CommonTextStyles.text20_800()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 20,
                                      ),
                                      Text(
                                        'VHDM Number: ${userId.vhdmNumber}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Member ID: ${userId.memberId}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Phone Number: ${userId.phoneNumber}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Email: ${userId.email}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'DOB: ${userId.dob}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Address: ${userId.address}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Adhar Card: ${userId.adharCard}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Blood Group: ${userId.bloodGroup}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 20,
                                      ),
                                      // Add more user data here
                                    ],
                                  ),
                                ),
                                SizedBox(
                                    width:
                                        16.0), // Add some spacing between the text and image
                                Stack(
                                    alignment: Alignment.bottomCenter,
                                    children: [
                                      Container(
                                        width: 77,
                                        height: 100,
                                        decoration: BoxDecoration(
                                          // color: Colors.red,

                                          image: DecorationImage(
                                            image:
                                                AssetImage('assets/logo.png'),
                                            fit: BoxFit.cover,
                                          ),
                                        ),
                                      ),
                                      Container(
                                        width: 89,
                                        height: 20,
                                        color: ColorConstants.quaternaryColor,
                                        alignment: Alignment.center,
                                        child: Text(
                                          "Member",
                                          style: TextStyle(color: Colors.black),
                                        ),
                                      )
                                    ]),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 40,
                ),
                CommonElevatedButton.buttonWithoutIcon(
                  title: "Download Id",
                  //the function where i am trying to create a pdf and save it.
                  onButtonPressed: () async {
                    final pdf = pw.Document();
                    final font =
                        await rootBundle.load("assets/Roboto/Roboto-Black.ttf");
                    final ttf = pw.Font.ttf(font);
                    pdf.addPage(pw.Page(
                      pageFormat: PdfPageFormat.a4,
                      build: (pw.Context context) {
                        return pw.Center(
                          child: pw.Text('Dart is awesome',
                              style: pw.TextStyle(font: ttf, fontSize: 40)),
                        );
                      },
                    ));

                    // Request necessary permissions
                    final status = await Permission.storage.request();
                    if (status.isGranted) {
                      // Get the directory path
                      final directoryPath =
                          await FilePicker.platform.getDirectoryPath();
                      if (directoryPath != null) {
                        final directory = Directory(directoryPath);
                        final fileName = 'example.pdf';
                        final filePath = path.join(directory.path, fileName);
                        final file = File(filePath);

                        await file.writeAsBytes(await pdf.save());

                        print('PDF saved to $filePath');
                      } else {
                        print('No folder selected');
                      }
                    } else {
                      print('Permission denied');
                    }
                  },
                )
              ])),
        ));
  }
}
flutter dart flutter-dependencies
2个回答
0
投票

//不确定是否必须将其保存在该文件目录中。只有当我尝试存储的文件名末尾没有 .pdf 时才会遇到这个问题,因此它不知道它实际使用的文档类型。

class DownloadPDF extends StatefulWidget {
  const DownloadPDF({super.key
    });

  @override
  State<DownloadPDF> createState() => _DownloadPDFState();
}

class _DownloadPDFState extends State<DownloadPDF> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
          onPressed: () => createPDF(), child: const Icon(Icons.download)),
    );
    }

  Future<Uint8List?> createPDF() async {
    final pdf = pw.Document();
    List headers = [
            "Property",
            "Value",
        ];
    List values = [CustomRow(name: "testing", value: "value")
        ];
    final data = values.map((e) => [e.name, e.value
        ]).toList();
    pdf.addPage(pw.MultiPage(
        build: (context) => [
              pw.Column(children: [
                pw.SizedBox(height: 100),
                pw.Text(
                    'Dart is awesome',
                    style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
            ]),
              pw.SizedBox(height: 100),
              pw.TableHelper.fromTextArray(
                headers: headers,
                data: data,
                tableWidth: pw.TableWidth.max,
                border: pw.TableBorder.all(
                  color: PdfColors.black,
                  width: 1.0,
                ),
              ),
        ]));
    List<int> bytes = await pdf.save();
    saveAndLaunchFile(bytes, 'PDF Document.pdf');
    return pdf.save();
    }

  Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
    bool dirDownloadExists = true;
    String androidDirectory;
    androidDirectory = "/storage/emulated/0/Download/";

    dirDownloadExists = await Directory(androidDirectory).exists();

    if (dirDownloadExists) {
      androidDirectory = "/storage/emulated/0/Download";
        } else {
      androidDirectory = "/storage/emulated/0/Downloads";
        }
    final path = Platform.isAndroid
        ? androidDirectory
        : (await getApplicationDocumentsDirectory()).path;
    final file = File('$path/$fileName');
    await file.writeAsBytes(bytes, flush: true);
    OpenFile.open('$path/$fileName');
    }
}

class CustomRow {
  final String name;
  final String value;

  CustomRow({required this.name, required this.value
    });
}

//如果您希望文件在下载后立即打开,您可以使用 OPENFILE PLUS,但需要将一些内容添加到您的 Android 清单中

   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools" package="xxx.xxx.xxxxx">
<application>
    ...
    <provider android:name="androidx.core.content.FileProvider"
              android:authorities="${applicationId}.fileProvider"
              android:exported="false"
              android:grantUriPermissions="true"
              tools:replace="android:authorities">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                   android:resource="@xml/filepaths"
                   tools:replace="android:resource" />
    </provider>
</application>

确保将 xxx.xxx.xxxxx 替换为您的包名称

这适用于 iOS 和 Android

希望这有帮助...


0
投票

我通过将 AndroidManifest 中

android:requestLegacyExternalStorage
标记内的
<application>
字段从
false
更改为
true
解决了该问题。从头开始重新编译后,就成功了。如果没有,请尝试卸载应用程序并重新编译。

© www.soinside.com 2019 - 2024. All rights reserved.