React Native文件系统与文件上传下载组件react-native-fs(适配Android & iOS)

可以实现文件的创建、删除、复制、移动、下载、上传等功能。

项目地址:https://github.com/johanneslumpe/react-native-fs

安装引用

安装组件

npm install react-native-fs --save

安装依赖

rnpm link

注:该命令需要重新编译项目,并且首先需要安装 rmpm

npm install rnpm -g

Android:注册模块(需重新编译)

@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
        new MainReactPackage(), new RNFSPackage()
    );
}

iOS:手动添加库

node_modules/react-native-fs/react-native-fs/RNFS.xcodeproj

API 介绍

常量

  • MainBundlePath (String):The absolute path to the main bundle directory
  • CachesDirectoryPath (String):The absolute path to the caches directory
  • DocumentDirectoryPath (String):The absolute path to the document directory
  • TemporaryDirectoryPath (String):The absolute path to the temporary directory (iOS only)
  • ExternalDirectoryPath (String):The absolute path to the external files, shared directory (android only)
  • ExternalStorageDirectoryPath (String):The absolute path to the external storage, shared directory (android only)

Android 下输出(小米4):

主要bundle目录:undefined
缓存目录:/data/user/0/com.awesomeproject/cache
文档目录:/data/user/0/com.awesomeproject/files
临时目录iOS:null
外部存储目录Android:/storage/emulated/0/Android/data/com.awesomeproject/files
图片目录:/storage/emulated/0/Pictures

方法

(1)readDir(dirpath: string): Promise<ReadDirItem[]>

读取目录内容,返回

type ReadDirItem = {
  name: string;     // The name of the item
  path: string;     // The absolute path to the item
  size: string;     // Size in bytes
  isFile: () => boolean;        // Is the file just a file?
  isDirectory: () => boolean;   // Is the file a directory?
};

(2)readdir(dirpath: string): Promise<string[]>

读取目录内容,返回

(3)stat(filepath: string): Promise<StatResult>

统计文件信息,返回文件属性

type StatResult = {
  name: string;     // The name of the item
  path: string;     // The absolute path to the item
  size: string;     // Size in bytes
  mode: number;     // UNIX file mode
  isFile: () => boolean;        // Is the file just a file?
  isDirectory: () => boolean;   // Is the file a directory?
};

(4)readFile(filepath: string, encoding?: string): Promise<string>

读取文件内容,编码可为:utf8 (default), ascii, base64

(5)writeFile(filepath: string, contents: string, encoding?: string): Promise<void>

写入文件内容,编码可为:utf8 (default), ascii, base64

(6)appendFile(filepath: string, contents: string, encoding?: string): Promise<void>

写入文件内容,编码可为:utf8 (default), ascii, base64

(7)moveFile(filepath: string, destPath: string): Promise<void>

移动文件

(8)copyFile(filepath: string, destPath: string): Promise<void>

复制文件

(9)unlink(filepath: string): Promise<void>

删除文件

(10)exists(filepath: string): Promise<boolean>

判断文件是否存在

(11)mkdir(filepath: string, options?: MkdirOptions): Promise<void>

创建目录,该命令会自动创建父目录,并且在目录已存在的情况下也不会抛出异常

MkdirOptions 只在 iOS 平台需要设置:

type MkdirOptions = {
    NSURLIsExcludedFromBackupKey?: boolean; // iOS only
};

(12)downloadFile(options: DownloadFileOptions): { jobId: number, promise: Promise<DownloadResult> }

下载文件,适配 Android & iOS 平台

传入参数:

type DownloadFileOptions = {
  fromUrl: string;          // URL to download file from
  toFile: string;           // Local filesystem path to save the file to
  headers?: Headers;        // An object of headers to be passed to the server
  background?: boolean;
  progressDivider?: number;
  begin?: (res: DownloadBeginCallbackResult) => void;
  progress?: (res: DownloadProgressCallbackResult) => void;
};

返回参数:

type DownloadResult = {
  jobId: number;          // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
  statusCode: number;     // The HTTP status code
  bytesWritten: number;   // The number of bytes written to the file
};

下载链接options.fromUrl的内容到文件options.toFile中,将会覆盖已存在的文件。

  • options.begin?

下载文件开始的回调函数,可用于获取一些初始化信息,如任务id、内容长度等。

type DownloadBeginCallbackResult = {
  jobId: number;          // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
  statusCode: number;     // The HTTP status code
  contentLength: number;  // The total size in bytes of the download resource
  headers: Headers;       // The HTTP response headers from the server
};
  • options.progress?

下载过程回调函数,可用于获取实时下载进度信息。

type DownloadProgressCallbackResult = {
  jobId: number;          // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
  contentLength: number;  // The total size in bytes of the download resource
  bytesWritten: number;   // The number of bytes written to the file so far
};
  • options.progressDivider

下载过程 progressCallback 回调频率,默认值为 0

如果 progressDivider = 10,将只接收10次 progressCallback 回调:0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100;如果 progressDivider = 0,将接收所有 progressCallback 回调。

  • options.background

只在 iOS 平台有效,默认值为 false

当 APP 失去焦点时是否后台继续下载

(13)stopDownload(jobId: number): Promise<void>

停止下载文件

(14)uploadFiles(options: UploadFileOptions): { jobId: number, promise: Promise<UploadResult> }

上传文件,只适用于 iOS 平台

传入参数:

type UploadFileOptions = {
  toUrl: string;            // URL to upload file to
  files: UploadFileItem[];  // An array of objects with the file information to be uploaded.
  headers?: Headers;        // An object of headers to be passed to the server
  fields?: Fields;          // An object of fields to be passed to the server
  method?: string;          // Default is 'POST', supports 'POST' and 'PUT'
  begin?: (res: UploadBeginCallbackResult) => void;
  progress?: (res: UploadProgressCallbackResult) => void;
};

文件结构对象 UploadFileItem:

type UploadFileItem = {
  name: string;       // Name of the file, if not defined then filename is used
  filename: string;   // Name of file
  filepath: string;   // Path to file
  filetype: string;   // The mimetype of the file to be uploaded, if not defined it will get mimetype from `filepath` extension
};

返回参数:

type UploadResult = {
  jobId: number;        // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`.
  statusCode: number;   // The HTTP status code
  headers: Headers;     // The HTTP response headers from the server
  body: string;         // The HTTP response body
};
  • options.begin

上传文件开始的回调函数,可用于获取一些初始化信息,如任务id

type UploadBeginCallbackResult = {
  jobId: number;        // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`.
};
  • options.progress

上传过程回调函数,可用于获取实时上传进度信息。

type UploadProgressCallbackResult = {
  jobId: number;                      // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`.
  totalBytesExpectedToSend: number;   // The total number of bytes that will be sent to the server
  totalBytesSent: number;             // The number of bytes sent to the server
};

(15)stopUpload(jobId: number): Promise<void>

停止上传文件,只适用于 iOS 平台

(16)getFSInfo(): Promise<FSInfoResult>

返回存储空间信息

type FSInfoResult = {
  totalSpace: number;   // The total amount of storage space on the device (in bytes).
  freeSpace: number;    // The amount of available storage space on the device (in bytes).
};

范例源码

index.android.js & index.ios.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View
} from 'react-native';

// let RNFS = require('react-native-fs');
import RNFS from 'react-native-fs';

let downloadUrl = 'http://dldir1.qq.com/qqfile/qq/QQ8.6/18804/QQ8.6.exe';
let jobId1 = -1, jobId2 = -1;
let testImage1Path = RNFS.PicturesDirectoryPath + '/test-image-11.png';

class FsDemo extends Component {
    constructor(props) {
        super(props);//这一句不能省略,照抄即可
            this.state = {
            output:  '文档目录路径: ' + RNFS.DocumentDirectoryPath,
        };
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    React Native RNFS 文件管理
                </Text>
                <Text style={styles.text}>{this.state.output}</Text>

                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.basic}>列出文件</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.createFile}>创建文件</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.delFile}>删除文件</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.printPath}>输出各种路径</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.downloadFile.bind(this, true, downloadUrl) }>下载文件</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.stopDownload}>停止下载</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.getFileInfo}>获取文件大小信息</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.uploadFile}>上传文件iOS</Text>
                <Text style={{ fontSize: 25, marginTop: 5, marginBottom: 5 }} onPress={this.stopUpload}>停止上传iOS</Text>

                <Text style={styles.author}>Powered by RNAPP.CC</Text>
            </View>
        );
    }

    basic = () => {
        // get a list of files and directories
        RNFS.readDir(RNFS.PicturesDirectoryPath).then(
            (result) => {
                console.log('GOT RESULT', result);
                // stat the first file
                return Promise.all([RNFS.stat(result[0].path), result[0].path]);
            })
        .then(
            (statResult) => {
                if (statResult[0].isFile()) {
                    // if we have a file, read it
                    return RNFS.readFile(statResult[1], 'utf8');
                }
                return 'no file';
            })
        .then((contents) => {
            // log the file contents
            console.log('文件内容:' + contents);
        })
        .catch((err) => {
            console.log('错误:' + err.message);
            console.log(err.message, err.code);
        });
    }

    downloadFile(background, url) {
        var progress = data => {
            var percentage = ((100 * data.bytesWritten) / data.contentLength) | 0;
            var text = `进度 ${percentage}%`;
            this.setState({
                output: text
            });
        };

        var begin = res => {
            jobId1 = res.jobId;
        };

        var progressDivider = 1;

        RNFS.downloadFile(
        {
            fromUrl: url,
            toFile: testImage1Path,
            begin,
            progress,
            background,
            progressDivider
        }).promise.then(
            res => {this.setState({
                output: JSON.stringify(res)
            });
        }).catch(
            err => this.showError(err)
        );
    }

    stopDownload = () => {
        console.log('停止下载');
        RNFS.stopDownload(jobId1);
    }

    showError = (err) => {
        console.log(err.message);
    }

    createFile = () => {
        // create a path you want to write to
        let path = RNFS.PicturesDirectoryPath + '/test.txt';

        // write the file
        RNFS.writeFile(path, 'Lorem ipsum dolor sit amet', 'utf8')
            .then((success) => {
                console.log('FILE WRITTEN! 目录是: ' + path);
            })
            .catch((err) => {
                console.log(err.message);
            });
    }

    delFile = () => {
        // create a path you want to delete
        let path = RNFS.PicturesDirectoryPath + '/test.txt';

        return RNFS.unlink(path)
            .then(() => {
                console.log('FILE DELETED');
            })
            // `unlink` will throw an error, if the item to unlink does not exist
            .catch((err) => {
                console.log(err.message);
            });
    }

    uploadFile = () => {
        // For testing purposes, go to http://requestb.in/ and create your own link
        var uploadUrl = 'http://requestb.in/1mhbfei1';
        // create an array of objects of the files you want to upload
        var files = [
            {
                name: 'test1',
                filename: 'test1.w4a',
                filepath: RNFS.PicturesDirectoryPath + '/test1.w4a',
                filetype: 'audio/x-m4a'
            }, {
                name: 'test2',
                filename: 'test2.w4a',
                filepath: RNFS.PicturesDirectoryPath + '/test2.w4a',
                filetype: 'audio/x-m4a'
            }
        ];

        var uploadBegin = (response) => {
            jobId2 = response.jobId;
            console.log('UPLOAD HAS BEGUN! JobId: ' + jobId2);
        };

        var uploadProgress = (response) => {
            var percentage = Math.floor((response.totalBytesSent / response.totalBytesExpectedToSend) * 100);
            console.log('UPLOAD IS ' + percentage + '% DONE!');
        };

        // upload files
        RNFS.uploadFiles(
            {
                toUrl: uploadUrl,
                files: files,
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                },
                fields: {
                    'hello': 'world',
                },
                begin: uploadBegin,
                progress: uploadProgress
            }
            ).promise.then((response) => {
                if (response.statusCode == 200) {
                    console.log('FILES UPLOADED!'); // response.statusCode, response.headers, response.body
                } else {
                    console.log('SERVER ERROR');
                }
            })
            .catch((err) => {
                if (err.description === "cancelled") {
                    // cancelled by user
                }
                console.log(err);
            });
    }

    stopUpload = () => {
        console.log('停止上传');
    }

    printPath = () => {
        console.log('主要bundle目录:' + RNFS.MainBundlePath); //undefined
        console.log('缓存目录:' + RNFS.CachesDirectoryPath);
        console.log('文档目录:' + RNFS.DocumentDirectoryPath);
        console.log('临时目录iOS:' + RNFS.TemporaryDirectoryPath); //null
        console.log('外部存储目录Android:' + RNFS.ExternalDirectoryPath);
        console.log('图片目录:' + RNFS.PicturesDirectoryPath);
    }

    getFileInfo = () => {
        return RNFS.getFSInfo().then(info => {
            this.setState({
                output: JSON.stringify(info)
            });
        });
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    flex: {
        flex: 1,
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
    },
    text: {
        fontSize: 16,
        textAlign: 'center',
        margin: 10,
        color:'red',
    },
    author: {
        justifyContent: 'flex-end',
        alignItems: 'center',
        marginBottom: 10,
        marginTop: 50,
        textAlign: 'center',
        fontSize: 16,
        color: '#3BC1FF',
    },
});

AppRegistry.registerComponent('AwesomeProject', () => FsDemo);

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/react-native-file-system-and-file-upload-download-component-react-native-fs-compatible-with-android-ios/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
React Native文件系统与文件上传下载组件react-native-fs(适配Android & iOS)
可以实现文件的创建、删除、复制、移动、下载、上传等功能。 项目地址:https://github.com/johanneslumpe/react-native-fs 安装引用 安装组件 npm install r……
<<上一篇
下一篇>>
文章目录
关闭
目 录