web-dev-qa-db-ja.com

Expo.FileSystem.downloadAsyncはダウンロード通知を表示しません

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

これには1つか2つのトリックがありましたが、iOSとAndroidの両方で機能するExpoを使用したこれに対する解決策を次に示します。

新しいExpoプロジェクトで、次の2つのファイルを修正します。

  • 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',
  },
});
"react-native-easy-toast": "git+https://github.com/SiavasFiroozbakht/react-native-easy-toast.git"

このソリューションに関する重要な注意事項がいくつかあります。

  • 外部ローカル通知およびファイルへの書き込み/ファイルからの読み取りにExpoAPIを最も多く使用します。これにより、現在のソリューションが制限されます 不可能に Expo自身のディレクトリ以外の場所に書き込むことができます。

  • ファイルがダウンロードされると、アプリがアクティブな場合はカスタマイズ可能なトーストがユーザーに表示されるか(Expoは現在フォアグラウンド通知をサポートしていません)、ローカルのプッシュ通知を送信してダウンロードが完了したことをユーザーに通知します。これら2つのいずれかをクリックすると、<Text>コンポーネントを使用して、ファイルの内容がビューに表示されます。

  • トーストの制限により、crazycodeboy/react-native-easy-toastリポジトリは直接使用されていません。つまり、タッチイベントは現在無視されています。フォークされたリポジトリは、マージ要求が元のリポジトリに実装される前に、この機能を利用できるようにします。パッチが適用されたら、元のバージョンに戻すことをお勧めします。

  • このプロジェクトは 利用可能 スナックではありますが、 gitリポジトリを使用 上記のpackage.jsonの必要性、およびその他の明らかな不整合のために実行されません。可変スコープで。これは、マージリクエストまたはSnackの新機能のいずれかによって修正されます。

  • 他のファイルタイプは、 Expo自体による または this PDF Viewer などの外部パッケージを介して)サポートされる場合があります。ただし、コードはさらに適応させる必要があります。

  • トースト(内部通知)は TouchableWithoutFeedback コンポーネントで作成されますが、 他の同様のもの in React Nativeこのコンポーネントはコードでカスタマイズできますが(toastDOMを検索)、 将来的にはExpoで利用可能な内部通知によって置き換えられる可能性もあります

  • 最後に、ファイルがダウンロードされると、意図的に3秒の遅延が通知に適用されます。これにより、アプリがバックグラウンドにあるときに通知をテストできます。遅延を削除して、 通知をすぐにトリガーしてください

以上です!これは、Expoでファイルをダウンロードしてプレビューするための良い出発点になると思います。

コードベース GitHubでも利用可能

4
Siavas

ダウンロードマネジャー

Androidでダウンロードを処理するために DownloadManager を使用することを検討していると思います(iOS用のDownloadManagerがないため、これを別の方法で処理する必要があることに注意してください) DownloadManagerは、ファイルを共有システムキャッシュに保存するか、外部ストレージに保存します。

ただし、現時点では、ExpoでDownloadManagerを使用できるとは思わず、代わりにすべてのダウンロード自体を処理します。その理由は、ドキュメントに記載されているように、Expoが外部ストレージへのアクセスを許可していないことが原因である可能性があります。

各アプリには、次のディレクトリの下の場所への読み取りおよび書き込みアクセスのみがあります。

  • Expo.FileSystem.documentDirectory
  • Expo.FileSystem.cacheDirectory

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

したがって、ダウンロードしたファイルにExpoでアクセスする方法はありません。

考えられる解決策

考えられる解決策は、 React-Native Fetch Blob を使用することです。 DownloadManagerを使用することはできます。 https://github.com/joltup/rn-fetch-blob#Android-media-scanner-and-download-manager-support

DownloadManagerの使用は、addAndroidDownloadsキーをtrueに設定してuseDownloadManagerを設定することにより、rn-fetch-blobで実現できます。

ただし、これは、Expoからアプリケーションをejectingすることを意味します。

2
Andrew