Первая версия мобильного приложения
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user