Expo.FileSystem.downloadAsync不显示下载通知

Car*_*los 7 react-native expo

我正在使用expo FileSystem下载pdf文件.API响应成为成功函数.但是,我无法将下载的文件显示给用户.

预期的行为应该像我们通常在状态栏上看到通知图标一样,点击图标会打开您的文件.

FileSystem.downloadAsync(
  'https://bitcoin.org/bitcoin.pdf',
  FileSystem.documentDirectory + 'Stay_Overview.xlsx'
).then(({ uri }) => {
   console.log('Finished downloading to ', uri);
})
 .catch(error => {
    console.error(error);
 });
Run Code Online (Sandbox Code Playgroud)

And*_*rew 6

下载管理器

我相信您希望使用DownloadManager来处理 Android 上的下载(请注意,iOS 上没有 DownloadManager,因此您必须以不同的方式处理此问题) DownloadManager 要么将文件保存到共享系统缓存,要么将其保存到外部存储。

但是,目前我不相信 Expo 允许您使用 DownloadManager,而是自行处理所有下载。原因可能是 Expo 不允许您访问外部存储,如文档中所述:

每个应用程序仅对以下目录下的位置具有读写访问权限:

  • Expo.文件系统.文档目录
  • Expo.FileSystem.cacheDirectory

https://docs.expo.io/versions/latest/sdk/filesystem

因此,一旦下载该文件,就无法在 Expo 中访问该文件。

可能的解决方案

一种可能的解决方案是使用React-Native Fetch Blob。它确实允许您使用下载管理器。 https://github.com/joltup/rn-fetch-blob#android-media-scanner-and-download-manager-support

使用 DownloadManager 可以通过rn-fetch-blobaddAndroidDownloadsuseDownloadManager设置为 true 来实现。

然而,这意味着您的申请将被从世博会中剔除。


Sia*_*vas 6

这个有一个或两个技巧,但这里有一个使用 Expo 的解决方案,适用于 iOS 和 Android。

在新建的 Expo 项目中,修改以下两个文件:

  • 应用程序.js
import React, { Component } from 'react';
import { View, ScrollView, StyleSheet, Button, Alert, Platform, Text, TouchableWithoutFeedback } from 'react-native';
import { FileSystem, Constants, Notifications, Permissions } from 'expo';
import Toast, {DURATION} from 'react-native-easy-toast';

async function getiOSNotificationPermission() {
  const { status } = await Permissions.getAsync(
    Permissions.NOTIFICATIONS
  );
  if (status !== 'granted') {
    await Permissions.askAsync(Permissions.NOTIFICATIONS);
  }
}

export default class App extends Component {
  constructor(props) {
    super(props);
    // this.toast = null;
    this.listenForNotifications = this.listenForNotifications.bind(this);
    // this.openFile = this.openFile.bind(this);
    this.state = {
      filePreviewText: ''
    }
  }

  _handleButtonPress = () => {
    let fileName = 'document.txt';
    let fileUri = FileSystem.documentDirectory + fileName;
    FileSystem.downloadAsync(
      "https://raw.githubusercontent.com/expo/expo/master/README.md",
      fileUri
    ).then(({ uri }) => {
      console.log('Finished downloading to ', uri);

      const localnotification = {
        title: 'Download has finished',
        body: fileName + " has been downloaded. Tap to open file.",
        android: {
          sound: true,
        },
        ios: {
          sound: true,
        },

        data: {
          fileUri: uri
        },
      };
      localnotification.data.title = localnotification.title;
      localnotification.data.body = localnotification.body;
      let sendAfterFiveSeconds = Date.now();
      sendAfterFiveSeconds += 3000;

      const schedulingOptions = { time: sendAfterFiveSeconds };
      Notifications.scheduleLocalNotificationAsync(
        localnotification,
        schedulingOptions
      );
    })
    .catch(error => {
        console.error(error);
        Alert.alert(error);
    });
  };
  listenForNotifications = () => {
    const _this = this;

    Notifications.addListener(notification => {
      if (notification.origin === 'received') {
        // We could also make our own design for the toast
        // _this.refs.toast.show(<View><Text>hello world!</Text></View>);

        const toastDOM = 
          <TouchableWithoutFeedback 
            onPress={() => {this.openFile(notification.data.fileUri)}}
            style={{padding: '10', backgroundColor: 'green'}}>
            <Text style={styles.toastText}>{notification.data.body}</Text>
          </TouchableWithoutFeedback>;

        _this.toast.show(toastDOM, DURATION.FOREVER);
      } else if (notification.origin === 'selected') {
        this.openFile(notification.data.fileUri);
      }
        // Expo.Notifications.setBadgeNumberAsync(number);
        // Notifications.setBadgeNumberAsync(10);
        // Notifications.presentLocalNotificationAsync(notification);
        // Alert.alert(notification.title, notification.body);
    });
  };
  componentWillMount() {
    getiOSNotificationPermission();
    this.listenForNotifications();
  }
  componentDidMount() {
    // let asset = Asset.fromModule(md);
    // Toast.show('Hello World');
  }
  openFile = (fileUri) => {
    this.toast.close(40);
    console.log('Opening file ' + fileUri);
    FileSystem.readAsStringAsync(fileUri)
    .then((fileContents) => {
      // Get file contents in binary and convert to text
      // let fileTextContent = parseInt(fileContents, 2);
      this.setState({filePreviewText: fileContents});
    });
  }
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.buttonsContainer}>
          <Button style={styles.button}
            title={"Download text file"}
            onPress={this._handleButtonPress}
          />
          <Button style={styles.button}
            title={"Clear File Preview"}
            onPress={() => {this.setState({filePreviewText: ""})}}
          />
        </View>
        <ScrollView style={styles.filePreview}>
          <Text>{this.state.filePreviewText}</Text>
        </ScrollView>
        <Toast ref={ (ref) => this.toast=ref }/>
      </View>
    );
            // <Toast
        //   ref={ (ref) => this.toast=ref }
        //   style={{backgroundColor:'green'}}
        //   textStyle={{color:'white'}}
        //   position={'bottom'}
        //   positionValue={100}
        //   opacity={0.8}
        // />
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
  },
  buttonsContainer: {
    flexDirection: 'row',
  },
  button: {
    flex: 1
  },
  filePreview: {
    flex: 1,
    padding: 10,
  },
  toastText: {
    color: 'white',
    padding: 5,
    justifyContent: 'flex-start',
  },
});
Run Code Online (Sandbox Code Playgroud)
"react-native-easy-toast": "git+https://github.com/SiavasFiroozbakht/react-native-easy-toast.git"
Run Code Online (Sandbox Code Playgroud)

关于这个解决方案有几个重要的注意事项:

  • 最多使用 Expo API,用于外部本地通知和写入/读取文件,这限制了当前的解决方案无法写入除了 Expo 自己的目录之外的其他位置。

  • 下载文件后,如果应用程序处于活动状态(Expo 目前不支持前台通知),则向用户显示可自定义的 toast,或者发送本地推送通知,让用户知道下载已完成。单击这两个中的任何一个都将使用该<Text>组件在视图中显示文件的内容。

  • crazycodeboy/react-native-easy-toast由于 toast 的限制,该repo 尚未直接使用,即当前忽略触摸事件。在合并请求在原始版本中实现之前,分叉存储库使此功能可用。我建议在修补后切换回原始版本,因为我可能不会维护我的。

  • 虽然这个项目也可以在 Snack 中使用,但由于需要在上面提到的 git 存储库package.json,以及变量作用域中的其他明显不一致,它不会运行。这将通过合并请求或 Snack 中的新功能来解决。

  • Expo 本身或通过外部软件包(例如此 PDF 查看器)可能支持其他文件类型。但是,必须进一步调整代码。

  • Toast(内部通知)是用一个TouchableWithoutFeedback组件创建的,尽管React Native 中还有其他类似的,但有各种不同。该组件可以在代码中自定义(搜索toastDOM),但将来甚至可能被Expo 中可用的内部通知替换

  • 最后,一旦下载文件,就会有意地将三秒延迟应用于通知——这允许我们在应用程序处于后台时测试通知。随意删除延迟并立即触发通知

就是这样!我认为这为使用 Expo 下载和预览文件提供了一个很好的起点。

代码库也可在 GitHub 上获得