Files
han-app/VM4_Expo-mobile/src/native-files.ts
T

97 lines
2.8 KiB
TypeScript

import * as Crypto from "expo-crypto";
import * as DocumentPicker from "expo-document-picker";
import { File, Paths } from "expo-file-system";
import * as FileSystem from "expo-file-system/legacy";
import * as Linking from "expo-linking";
import * as Sharing from "expo-sharing";
export type NativeFile = {
uri: string;
name: string;
mimeType: string;
size: number;
};
type PickedAsset = {
uri: string;
name: string;
mimeType?: string | null;
size?: number | null;
};
export function toNativeFile(asset: PickedAsset): NativeFile {
return {
uri: asset.uri,
name: asset.name,
mimeType: asset.mimeType ?? "application/octet-stream",
size: asset.size ?? 0,
};
}
export async function pickFiles({
multiple = false,
mimeTypes,
}: {
multiple?: boolean;
mimeTypes?: string[] | undefined;
} = {}): Promise<NativeFile[]> {
const result = await DocumentPicker.getDocumentAsync({
copyToCacheDirectory: true,
multiple,
type: mimeTypes?.length ? mimeTypes : "*/*",
});
if (result.canceled) return [];
return result.assets.map((asset) => {
const file = toNativeFile(asset);
if (file.size > 0) return file;
const actualSize = new File(file.uri).size;
return {
...file,
size: typeof actualSize === "number" && Number.isFinite(actualSize)
? actualSize
: file.size,
};
});
}
export async function sha256(file: NativeFile) {
const bytes = await new File(file.uri).bytes();
const digest = await Crypto.digest(Crypto.CryptoDigestAlgorithm.SHA256, bytes);
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
return `sha256:${hex}`;
}
export async function uploadFile(
url: string,
file: NativeFile,
headers: Record<string, string> = {},
) {
const task = FileSystem.createUploadTask(url, file.uri, {
httpMethod: "PUT",
headers: {
"Content-Type": file.mimeType,
...headers,
},
uploadType: FileSystem.FileSystemUploadType.BINARY_CONTENT,
});
const result = await task.uploadAsync();
if (!result || result.status < 200 || result.status >= 300) {
throw new Error("Не удалось загрузить файл в хранилище");
}
}
export async function downloadAndOpen(url: string, suggestedName = "document") {
try {
const safeName = suggestedName.replace(/[^\p{L}\p{N}._-]+/gu, "_") || "document";
const target = new File(Paths.cache, `${Date.now()}-${safeName}`).uri;
const result = await FileSystem.downloadAsync(url, target);
if (await Sharing.isAvailableAsync()) {
await Sharing.shareAsync(result.uri);
return;
}
} catch {
// Системный браузер остаётся безопасным запасным вариантом.
}
await Linking.openURL(url);
}