xref: /MusicFree/src/utils/fileUtils.ts (revision 6613e77203923e5b1742a49281bfa5de03fc1440)
1import pathConst from '@/constants/pathConst';
2import FastImage from 'react-native-fast-image';
3import RNFS, {
4    copyFile,
5    downloadFile,
6    exists,
7    mkdir,
8    PicturesDirectoryPath,
9    readDir,
10    unlink,
11    writeFile,
12} from 'react-native-fs';
13import {errorLog} from './log';
14import path from 'path-browserify';
15
16export const galleryBasePath = `${PicturesDirectoryPath}/MusicFree/`;
17
18export async function saveToGallery(src: string) {
19    const fileName = `${galleryBasePath}${Date.now()}.png`;
20    if (!(await exists(galleryBasePath))) {
21        await mkdir(galleryBasePath);
22    }
23    if (await exists(src)) {
24        try {
25            await copyFile(src, fileName);
26        } catch (e) {
27            console.log('... ', e);
28        }
29    }
30    if (src.startsWith('http')) {
31        await downloadFile({
32            fromUrl: src,
33            toFile: fileName,
34            background: true,
35        });
36    }
37    if (src.startsWith('data')) {
38        await writeFile(fileName, src);
39    }
40}
41
42export function sizeFormatter(bytes: number | string) {
43    if (typeof bytes === 'string') {
44        return bytes;
45    }
46    if (bytes === 0) {
47        return '0B';
48    }
49    let k = 1024,
50        sizes = ['B', 'KB', 'MB', 'GB'],
51        i = Math.floor(Math.log(bytes) / Math.log(k));
52    return (bytes / Math.pow(k, i)).toFixed(1) + sizes[i];
53}
54
55export async function checkAndCreateDir(dirPath: string) {
56    const filePath = dirPath;
57    try {
58        if (!(await exists(filePath))) {
59            await mkdir(filePath);
60        }
61    } catch (e) {
62        errorLog('无法初始化目录', {path: dirPath, e});
63    }
64}
65
66async function getFolderSize(dirPath: string): Promise<number> {
67    let size = 0;
68    try {
69        const fns = await readDir(dirPath);
70        for (let fn of fns) {
71            if (fn.isFile()) {
72                size += fn.size;
73            }
74            // todo: 可以改成并行 promise.all
75            if (fn.isDirectory()) {
76                size += await getFolderSize(fn.path);
77            }
78        }
79    } catch {}
80    return size;
81}
82
83export async function getCacheSize(
84    type: 'music' | 'lyric' | 'image',
85): Promise<number> {
86    if (type === 'music') {
87        return getFolderSize(pathConst.musicCachePath);
88    } else if (type === 'lyric') {
89        return getFolderSize(pathConst.lrcCachePath);
90    } else if (type === 'image') {
91        return getFolderSize(pathConst.imageCachePath);
92    }
93    throw new Error();
94}
95
96export async function clearCache(type: 'music' | 'lyric' | 'image') {
97    if (type === 'music') {
98        try {
99            if (await exists(pathConst.musicCachePath)) {
100                return unlink(pathConst.musicCachePath);
101            }
102        } catch {}
103    } else if (type === 'lyric') {
104        try {
105            const lrcs = readDir(pathConst.lrcCachePath);
106            return Promise.all((await lrcs).map(_ => unlink(_.path)));
107        } catch {}
108    } else if (type === 'image') {
109        return FastImage.clearDiskCache();
110    }
111}
112
113export function addFileScheme(fileName: string) {
114    if (fileName.startsWith('/')) {
115        return `file://${fileName}`;
116    }
117    return fileName;
118}
119
120export function addRandomHash(url: string) {
121    if (url.indexOf('#') === -1) {
122        return `${url}#${Date.now()}`;
123    }
124    return url;
125}
126
127export function trimHash(url: string) {
128    const index = url.lastIndexOf('#');
129    if (index === -1) {
130        return url;
131    }
132    return url.substring(0, index);
133}
134
135export function escapeCharacter(str?: string) {
136    return str !== undefined ? `${str}`.replace(/[/|\\?*"<>:]+/g, '_') : '';
137}
138
139export function getDirectory(dirPath: string) {
140    const lastSlash = dirPath.lastIndexOf('/');
141    if (lastSlash === -1) {
142        return dirPath;
143    }
144    return dirPath.slice(0, lastSlash);
145}
146
147export function getFileName(filePath: string, withoutExt?: boolean) {
148    const lastSlash = filePath.lastIndexOf('/');
149    if (lastSlash === -1) {
150        return filePath;
151    }
152    let fileName = filePath.slice(lastSlash + 1);
153    if (withoutExt) {
154        const lastDot = fileName.lastIndexOf('.');
155        fileName = lastDot === -1 ? fileName : fileName.slice(0, lastDot);
156    }
157
158    try {
159        return decodeURIComponent(fileName);
160    } catch {
161        return fileName;
162    }
163}
164
165export async function mkdirR(directory: string) {
166    let folder = directory;
167    const checkStack: string[] = [];
168    while (folder.length > 15) {
169        checkStack.push(folder);
170        folder = path.dirname(folder);
171    }
172    let existPos = 0;
173    for (let i = 0; i < checkStack.length; ++i) {
174        const isExist = await exists(checkStack[i]);
175        if (isExist) {
176            existPos = i;
177            break;
178        }
179    }
180
181    for (let j = existPos - 1; j >= 0; --j) {
182        try {
183            await mkdir(checkStack[j]);
184        } catch (e) {
185            console.log('error', e);
186        }
187    }
188}
189
190export async function writeInChunks(
191    filePath: string,
192    data,
193    chunkSize = 1024 * 1024 * 2,
194) {
195    let offset = 0;
196    console.log('here');
197    if (await exists(filePath)) {
198        await unlink(filePath);
199    }
200
201    while (offset < data.length) {
202        const chunk = data.slice(offset, offset + chunkSize);
203        if (offset === 0) {
204            await RNFS.writeFile(filePath, chunk, 'utf8');
205        } else {
206            await RNFS.appendFile(filePath, chunk, 'utf8');
207        }
208        offset += chunkSize;
209    }
210}
211