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

问题描述:

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

I am using expo FileSystem to download the pdf file. The API response lands into success function. However, I am not able to show the downloaded file to the user.

预期的行为应类似于我们通常在状态栏上看到通知图标,然后单击图标打开您的文件.

The expected behaviour should be like we usually see notification icon on the status bar and on click on icon its opens your file.

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);
 });

此方法有一个或两个技巧,但这是使用Expo的解决方案,该方法可在iOS和Android上使用.

This one had one or two tricks, but here is a solution to this using Expo that works on both iOS and Android.

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

In a new Expo project, amend the following two files:

  • App.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',
  },
});

  • package.json:添加以下依赖项( react-native-easy-吐司)
    • package.json: Add the following dependency (fork of react-native-easy-toast)
"react-native-easy-toast": "git+https://github.com/SiavasFiroozbakht/react-native-easy-toast.git"

有关此解决方案的一些重要说明:

There are a couple of important notes about this solution:

  • 最常使用Expo API,用于外部本地通知和文件读写,这限制了当前解决方案

  • Uses Expo API the most, for external local notifications and writing to / reading from files, which limits the current solution to being unable to write to other locations than Expo's own directory.

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

Once the file is downloaded, either a customisable toast is shown to the user if the app is active (Expo currently does not support foreground notifications), or sends a local Push Notification to let the user know the download has finished. Clicking on any of these two will show the contents of the file in a View, using the <Text> component.

由于烤面包的限制,尚未直接使用crazycodeboy/react-native-easy-toast存储库,这是当前不考虑触摸事件的原因.在原始请求中实现合并请求之前,分叉的仓库使此功能可用.我建议一旦修补后再切换回原始版本,因为我可能不会维护我的版本.

The crazycodeboy/react-native-easy-toast repo has not been used directly due to a limitation of the toast, which is that the touch events are currently disregarded. The forked repo makes this functionality available before the merge request is implemented in the original. I recommend switching back to the original one once it gets patched as I will likely not maintain mine.

尽管该项目在Snack中也可用,但不会由于需要使用git存储库而运行如上所述的package.json中的>,以及变量作用域中的其他明显不一致之处.这可以通过合并请求或Snack中的新功能来解决.

Although this project is also available in Snack, it will not run due to the need of using a git repository in package.json as mentioned above, and other apparent inconsistencies in variable scoping. This would be fixed by either the merge request or the new feature in Snack.

Expo本身可能会支持其他文件类型或通过外部软件包,例如此PDF查看器.但是,必须进一步修改代码.

Other file types may be supported, either by Expo itself or via external packages, such as this PDF viewer. However, the code will have to be further adapted.

吐司(内部通知)是使用 TouchableWithoutFeedback href ="https://facebook.github.io/react-native/docs/touchablewithoutfeedback"创建的. a>组件,尽管React Native中有其他类似的差异.可以在代码中自定义此组件(搜索toastDOM),但是

The toast (internal notification) is created with a TouchableWithoutFeedback component, although there are other similar ones in React Native with various differences. This component can be customised in the code (search for toastDOM), but might even be replaceable in the future by internal notifications available in Expo.

最后,文件下载后,通知会故意延迟三秒钟–这使我们可以在应用程序处于后台时测试通知.随时删除延迟并立即触发通知.. p>

Lastly, an intentional three-second delay is applied to the notification once the file is downloaded – this allows us to test the notification when the app is in background. Feel free to remove the delay and trigger the notification immediately.

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

And that's it! I think this gives a good starting point for file downloading and previewing with Expo.

代码库也在GitHub上提供.