1import AsyncStorage from '@react-native-community/async-storage';
2import { Asset } from 'expo-asset';
3import * as FileSystem from 'expo-file-system';
4import * as Progress from 'expo-progress';
5import React from 'react';
6import { Alert, ScrollView, StyleSheet } from 'react-native';
7
8import ListButton from '../components/ListButton';
9
10interface State {
11  downloadProgress: number;
12}
13
14export default class FileSystemScreen extends React.Component<object, State> {
15  static navigationOptions = {
16    title: 'FileSystem',
17  };
18
19  readonly state: State = {
20    downloadProgress: 0,
21  };
22
23  download?: FileSystem.DownloadResumable;
24
25  _download = async () => {
26    const url = 'http://ipv4.download.thinkbroadband.com/256KB.zip';
27    await FileSystem.downloadAsync(url, FileSystem.documentDirectory + '256KB.zip');
28    alert('Download complete!');
29  };
30
31  _startDownloading = async () => {
32    const url = 'http://ipv4.download.thinkbroadband.com/5MB.zip';
33    const fileUri = FileSystem.documentDirectory + '5MB.zip';
34    const callback: FileSystem.DownloadProgressCallback = downloadProgress => {
35      const progress =
36        downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite;
37      this.setState({
38        downloadProgress: progress,
39      });
40    };
41    const options = { md5: true };
42    this.download = FileSystem.createDownloadResumable(url, fileUri, options, callback);
43
44    try {
45      const result = await this.download.downloadAsync();
46      if (result) {
47        this._downloadComplete();
48      }
49    } catch (e) {
50      console.log(e);
51    }
52  };
53
54  _pause = async () => {
55    if (!this.download) {
56      alert('Initiate a download first!');
57      return;
58    }
59    try {
60      const downloadSnapshot = await this.download.pauseAsync();
61      await AsyncStorage.setItem('pausedDownload', JSON.stringify(downloadSnapshot));
62      alert('Download paused...');
63    } catch (e) {
64      console.log(e);
65    }
66  };
67
68  _resume = async () => {
69    try {
70      if (this.download) {
71        const result = await this.download.resumeAsync();
72        if (result) {
73          this._downloadComplete();
74        }
75      } else {
76        this._fetchDownload();
77      }
78    } catch (e) {
79      console.log(e);
80    }
81  };
82
83  _downloadComplete = () => {
84    if (this.state.downloadProgress !== 1) {
85      this.setState({
86        downloadProgress: 1,
87      });
88    }
89    alert('Download complete!');
90  };
91
92  _fetchDownload = async () => {
93    try {
94      const downloadJson = await AsyncStorage.getItem('pausedDownload');
95      if (downloadJson !== null) {
96        const downloadFromStore = JSON.parse(downloadJson);
97        const callback: FileSystem.DownloadProgressCallback = downloadProgress => {
98          const progress =
99            downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite;
100          this.setState({
101            downloadProgress: progress,
102          });
103        };
104        this.download = new FileSystem.DownloadResumable(
105          downloadFromStore.url,
106          downloadFromStore.fileUri,
107          downloadFromStore.options,
108          callback,
109          downloadFromStore.resumeData
110        );
111        await this.download.resumeAsync();
112        if (this.state.downloadProgress === 1) {
113          alert('Download complete!');
114        }
115      } else {
116        alert('Initiate a download first!');
117        return;
118      }
119    } catch (e) {
120      console.log(e);
121    }
122  };
123
124  _getInfo = async () => {
125    if (!this.download) {
126      alert('Initiate a download first!');
127      return;
128    }
129    try {
130      const info = await FileSystem.getInfoAsync(this.download._fileUri);
131      Alert.alert('File Info:', JSON.stringify(info), [{ text: 'OK', onPress: () => {} }]);
132    } catch (e) {
133      console.log(e);
134    }
135  };
136
137  _readAsset = async () => {
138    const asset = Asset.fromModule(require('../../assets/index.html'));
139    await asset.downloadAsync();
140    try {
141      const result = await FileSystem.readAsStringAsync(asset.localUri!);
142      Alert.alert('Result', result);
143    } catch (e) {
144      Alert.alert('Error', e.message);
145    }
146  };
147
148  _getInfoAsset = async () => {
149    const asset = Asset.fromModule(require('../../assets/index.html'));
150    await asset.downloadAsync();
151    try {
152      const result = await FileSystem.getInfoAsync(asset.localUri!);
153      Alert.alert('Result', JSON.stringify(result, null, 2));
154    } catch (e) {
155      Alert.alert('Error', e.message);
156    }
157  };
158
159  _copyAndReadAsset = async () => {
160    const asset = Asset.fromModule(require('../../assets/index.html'));
161    await asset.downloadAsync();
162    const tmpFile = FileSystem.cacheDirectory + 'test.html';
163    try {
164      await FileSystem.copyAsync({ from: asset.localUri!, to: tmpFile });
165      const result = await FileSystem.readAsStringAsync(tmpFile);
166      Alert.alert('Result', result);
167    } catch (e) {
168      Alert.alert('Error', e.message);
169    }
170  };
171
172  _alertFreeSpace = async () => {
173    const freeBytes = await FileSystem.getFreeDiskStorageAsync();
174    alert(`${Math.round(freeBytes / 1024 / 1024)} MB available`);
175  };
176
177  render() {
178    return (
179      <ScrollView style={{ padding: 10 }}>
180        <ListButton onPress={this._download} title="Download file (512KB)" />
181        <ListButton onPress={this._startDownloading} title="Start Downloading file (5MB)" />
182        <ListButton onPress={this._pause} title="Pause Download" />
183        <ListButton onPress={this._resume} title="Resume Download" />
184        <ListButton onPress={this._getInfo} title="Get Info" />
185        <Progress.Bar style={styles.progress} isAnimated progress={this.state.downloadProgress} />
186        <ListButton onPress={this._readAsset} title="Read Asset" />
187        <ListButton onPress={this._getInfoAsset} title="Get Info Asset" />
188        <ListButton onPress={this._copyAndReadAsset} title="Copy and Read Asset" />
189        <ListButton onPress={this._alertFreeSpace} title="Alert free space" />
190      </ScrollView>
191    );
192  }
193}
194
195const styles = StyleSheet.create({
196  progress: {
197    marginHorizontal: 10,
198    marginVertical: 32,
199  },
200});
201