- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我刚开始学习flutter。我用 Nodejs 编写了一个 api 并部署到 heroku。
我通过 Postman 和浏览器测试了 api,它适用于字符串、json 和 pdf 文件。
如何使 flutter 从 api 下载 pdf 文件
我的 nodejs api
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/getPdfFile/:Name', function(req, res, next) {
var path = require('path');
console.log(__dirname);
var file = path.join(__dirname, '../pdfs/'+req.params.Name+'.pdf');
res.download(file, function (err) {
if (err) {
console.log("Error");
console.log(err);
} else {
console.log("Success");
}
});
});
router.get('/getName', function(req, res, next) {
res.json({"foo": "bar"});
});
router.get('/getNamee', function(req, res, next) {
res.send('some string');
});
我的 flutter main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(new MaterialApp(
home: new HomePage(),
));
}
class HomePage extends StatefulWidget {
@override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> {
List data;
//apidenemee4.herokuapp.com/getPdfFile/SpaceX
Future getData() async {
var response = await http.get(
Uri.encodeFull("https://mynodeapi4.herokuapp.com//getPdfFile/SpaceX"),
headers: {"Accept": "application/json"});
print(response.body);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new RaisedButton(
child: new Text("Get data"),
onPressed: getData,
),
),
);
}
}
最佳答案
您可以使用 flutter_downloader
- 下载并打开文件,path_provider
- 访问设备路径和permission_handler
- 处理设备存储权限。
请在 AndroidManifest.xml
中添加以下权限适用于安卓设备
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
添加
provider
里面
application
标签
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.snj07.flutter_app_stw.flutter_downloader.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
使用
xml
创建一个文件夹里面的名字
src/main
并在
provider_paths.xml
中添加以下内容
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
请自定义以下示例以下载 PDF 文件并在您的项目中打开它。取自
flutter_downloader
例子。我还根据最新的插件更新了一些权限处理代码。
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
const debug = true;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(debug: debug);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(
title: 'Downloader',
platform: platform,
),
);
}
}
class MyHomePage extends StatefulWidget with WidgetsBindingObserver {
final TargetPlatform platform;
MyHomePage({Key key, this.title, this.platform}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _documents = [
{
'name': 'PDF',
'link':
'http://darwinlogic.com/uploads/education/iOS_Programming_Guide.pdf'
},
];
List<_TaskInfo> _tasks;
List<_ItemHolder> _items;
bool _isLoading;
bool _permissionReady;
String _localPath;
ReceivePort _port = ReceivePort();
@override
void initState() {
super.initState();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
_isLoading = true;
_permissionReady = false;
_prepare();
}
@override
void dispose() {
_unbindBackgroundIsolate();
super.dispose();
}
void _bindBackgroundIsolate() {
bool isSuccess = IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
_bindBackgroundIsolate();
return;
}
_port.listen((dynamic data) {
if (debug) {
print('UI Isolate Callback: $data');
}
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
final task = _tasks?.firstWhere((task) => task.taskId == id);
if (task != null) {
setState(() {
task.status = status;
task.progress = progress;
});
}
});
}
void _unbindBackgroundIsolate() {
IsolateNameServer.removePortNameMapping('downloader_send_port');
}
static void downloadCallback(
String id, DownloadTaskStatus status, int progress) {
if (debug) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
}
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: Builder(
builder: (context) => _isLoading
? new Center(
child: new CircularProgressIndicator(),
)
: _permissionReady
? new Container(
child: new ListView(
padding: const EdgeInsets.symmetric(vertical: 16.0),
children: _items
.map((item) => item.task == null
? new Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
child: Text(
item.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
fontSize: 18.0),
),
)
: new Container(
padding: const EdgeInsets.only(
left: 16.0, right: 8.0),
child: InkWell(
onTap: item.task.status ==
DownloadTaskStatus.complete
? () {
_openDownloadedFile(item.task)
.then((success) {
if (!success) {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text(
'Cannot open this file')));
}
});
}
: null,
child: new Stack(
children: <Widget>[
new Container(
width: double.infinity,
height: 64.0,
child: new Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new Text(
item.name,
maxLines: 1,
softWrap: true,
overflow:
TextOverflow.ellipsis,
),
),
new Padding(
padding:
const EdgeInsets.only(
left: 8.0),
child: _buildActionForTask(
item.task),
),
],
),
),
item.task.status ==
DownloadTaskStatus
.running ||
item.task.status ==
DownloadTaskStatus.paused
? new Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child:
new LinearProgressIndicator(
value: item.task.progress /
100,
),
)
: new Container()
]
.where((child) => child != null)
.toList(),
),
),
))
.toList(),
),
)
: new Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 24.0),
child: Text(
'Please grant accessing storage permission to continue -_-',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blueGrey, fontSize: 18.0),
),
),
SizedBox(
height: 32.0,
),
FlatButton(
onPressed: () {
_checkPermission().then((hasGranted) {
setState(() {
_permissionReady = hasGranted;
});
});
},
child: Text(
'Retry',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 20.0),
))
],
),
),
)),
);
}
Widget _buildActionForTask(_TaskInfo task) {
if (task.status == DownloadTaskStatus.undefined) {
return new RawMaterialButton(
onPressed: () {
_requestDownload(task);
},
child: new Icon(Icons.file_download),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.running) {
return new RawMaterialButton(
onPressed: () {
_pauseDownload(task);
},
child: new Icon(
Icons.pause,
color: Colors.red,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.paused) {
return new RawMaterialButton(
onPressed: () {
_resumeDownload(task);
},
child: new Icon(
Icons.play_arrow,
color: Colors.green,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.complete) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Text(
'Open',
style: new TextStyle(color: Colors.green),
),
RawMaterialButton(
onPressed: () {
_delete(task);
},
child: Icon(
Icons.delete_forever,
color: Colors.red,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else if (task.status == DownloadTaskStatus.canceled) {
return new Text('Canceled', style: new TextStyle(color: Colors.red));
} else if (task.status == DownloadTaskStatus.failed) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
new Text('Failed', style: new TextStyle(color: Colors.red)),
RawMaterialButton(
onPressed: () {
_retryDownload(task);
},
child: Icon(
Icons.refresh,
color: Colors.green,
),
shape: new CircleBorder(),
constraints: new BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else {
return null;
}
}
void _requestDownload(_TaskInfo task) async {
task.taskId = await FlutterDownloader.enqueue(
url: task.link,
headers: {"auth": "test_for_sql_encoding"},
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true);
}
void _cancelDownload(_TaskInfo task) async {
await FlutterDownloader.cancel(taskId: task.taskId);
}
void _pauseDownload(_TaskInfo task) async {
await FlutterDownloader.pause(taskId: task.taskId);
}
void _resumeDownload(_TaskInfo task) async {
String newTaskId = await FlutterDownloader.resume(taskId: task.taskId);
task.taskId = newTaskId;
}
void _retryDownload(_TaskInfo task) async {
String newTaskId = await FlutterDownloader.retry(taskId: task.taskId);
task.taskId = newTaskId;
}
Future<bool> _openDownloadedFile(_TaskInfo task) {
return FlutterDownloader.open(taskId: task.taskId);
}
void _delete(_TaskInfo task) async {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await _prepare();
setState(() {});
}
Future<bool> _checkPermission() async {
if (widget.platform == TargetPlatform.android) {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
if (statuses[Permission.storage] == PermissionStatus.denied) {
if (await Permission.contacts.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
Future<Null> _prepare() async {
final tasks = await FlutterDownloader.loadTasks();
int count = 0;
_tasks = [];
_items = [];
_tasks.addAll(_documents.map((document) =>
_TaskInfo(name: document['name'], link: document['link'])));
_items.add(_ItemHolder(name: 'Documents'));
for (int i = count; i < _tasks.length; i++) {
_items.add(_ItemHolder(name: _tasks[i].name, task: _tasks[i]));
count++;
}
tasks?.forEach((task) {
for (_TaskInfo info in _tasks) {
if (info.link == task.url) {
info.taskId = task.taskId;
info.status = task.status;
info.progress = task.progress;
}
}
});
_permissionReady = await _checkPermission();
_localPath = (await _findLocalPath()) + Platform.pathSeparator + 'Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
setState(() {
_isLoading = false;
});
}
Future<String> _findLocalPath() async {
final directory = widget.platform == TargetPlatform.android
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
return directory.path;
}
}
class _TaskInfo {
final String name;
final String link;
String taskId;
int progress = 0;
DownloadTaskStatus status = DownloadTaskStatus.undefined;
_TaskInfo({this.name, this.link});
}
class _ItemHolder {
final String name;
final _TaskInfo task;
_ItemHolder({this.name, this.task});
}
关于javascript - Flutter 如何从 api 下载 pdf 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63245116/
我正在尝试从flutter应用程序构建apk,但出现此错误: Note: /mnt/Software/Linux/Flutter/flutter/.pub-cache/hosted/pub.dartl
我有一个名为 X 的较大应用程序,还有另一个名为 Y 的较小应用程序。他们现在彼此分开,并且工作正常。我想将应用程序 Y 集成到 X 中。我想将 Y 的代码放入 X 项目中,但它们应该有不同的 Mai
在android Studio中选择Create New Flutter Project,出现如下4个选项。 Flutter 应用程序 Flutter 插件 Flutter 包 flutter 模块
我看到我的 flutter 项目生成了一个文件 ios/Flutter/Flutter.podspec ,这个文件有什么用? 如果它与生成的 Flutter.framework 有关? 我应该将它包含
我尝试过的 在包含flutter SDK的位置添加/编辑.bash_profile.rtf文件。 导出PATH = / Users / temur / Documents / Projects / F
Flutter 日志会打印数千个详细/垃圾邮件日志。 我正在尝试调试一个复杂的应用程序,但是打印太冗长了,以至于我很难找到我自己打印的东西。 有没有办法禁用详细? 就像是: Logger.level.
在flutter 1.22更新之后,我在Lineargradient colors属性中遇到错误,这给我一个错误,即未定义名称colors参数。.在Android中更新flutter和flutter插
在下面的代码 widget.hintText 中给出错误,我试图将日期选择器作为单独的组件,并在从其他文件调用它时动态传递提示文本值。 import 'package:date_field/date_
在下面的代码 widget.hintText 中给出错误,我试图将日期选择器作为单独的组件,并在从其他文件调用它时动态传递提示文本值。 import 'package:date_field/date_
Flutter 1.0 发布后,我正在按照步骤搭建 Flutter 开发环境。 在步骤中(如所附屏幕截图所示),它说要更新 $PATH 两次,一次使用 flutter 工具的路径 export PAT
我有一个用 flutter 编写的移动应用程序,我想将其转换为 flutter_web 应用程序(集成 flutter_web 尚不可用)。我目前遇到包裹问题。 我已按照本网站中列出的说明进行操作 h
如何向我的 Flutter 路由添加自定义转换?这是我目前的路线结构。 class MyApp extends StatelessWidget { // This widget is the
我正在尝试通过 URL 在 webview 中显示网页。我试过 flutter_webview_plugin 插件,但是当我在浏览器上运行项目时它不起作用。 在 flutter web applica
我正在使用 animatedContainer 在按下按钮时显示 listView.builder()。这是一个要显示的简单子(monad)列表,但问题是我不知道 ListView 构建器的高度会传递
我目前正在我的应用程序中制作渐变背景动画......我正在使用 lottie 动画的帮助下这样做!我试图将它封装在一个容器中并成功地做到了。但是有一个问题,尽管我将高度更改为大于 2000 的东西,但
美好的一天! 我无法弄清楚如何使用 google 标签管理器设置 flutter。我找到了 this package包括标签管理器 api。但是我不知道如何正确配置它。 (在网络上我只需要复制粘贴一个
我的购物车模型如下 class Cart { String description; double unitCost; double amount; int quantity; S
在 Flutter 应用程序中,我想为在线托管的资源(图像、视频等)实现缓存。 我希望它能在原生平台 (Android/iOS)(例如使用文件系统)和网络(例如使用 IndexedDB)上运行。 Fl
我写了一个页面,在顶部一切都很好,应该是这样。在底部我有一个事件的历史。我的容器的宽度是自动确定的(取决于屏幕的宽度),而高度 - 不,在不同的设备上有不同的高度(底部的缩进是不同的)。是否可以自动确
我正在处理一个页面,其中有一些字段,例如 textfield 和 slider。在页面的末尾必须有一个用于进行下一步的按钮,该按钮被包裹在 Align 中以在页面之间具有固定位置。 另一方面,resi
我是一名优秀的程序员,十分优秀!