Handle edge case validations
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
|||||||
usePhotoLibraryPermission,
|
usePhotoLibraryPermission,
|
||||||
useVideoLibraryPermission,
|
useVideoLibraryPermission,
|
||||||
} from '#/lib/hooks/usePermissions'
|
} from '#/lib/hooks/usePermissions'
|
||||||
import {extractDataUriMime} from '#/lib/media/util'
|
import {extractDataUriMime, getDataUriSize} from '#/lib/media/util'
|
||||||
import {mimeToExt} from '#/lib/media/video/util'
|
import {mimeToExt} from '#/lib/media/video/util'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {isIOS, isNative, isWeb} from '#/platform/detection'
|
import {isIOS, isNative, isWeb} from '#/platform/detection'
|
||||||
@@ -141,7 +141,7 @@ function classifyImagePickerAsset(asset: ImagePickerAsset):
|
|||||||
mimeType: undefined
|
mimeType: undefined
|
||||||
} {
|
} {
|
||||||
/*
|
/*
|
||||||
* Try to use the `mimeType` reported by Expo's library first.
|
* Try to use the `mimeType` reported by `expo-image-picker` first.
|
||||||
*/
|
*/
|
||||||
let mimeType = asset.mimeType
|
let mimeType = asset.mimeType
|
||||||
|
|
||||||
@@ -204,16 +204,6 @@ function classifyImagePickerAsset(asset: ImagePickerAsset):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dataURItoBlob(uri: string, mimeType: string) {
|
|
||||||
const [, data] = uri.split(',')
|
|
||||||
const binary = atob(data)
|
|
||||||
const array = new Uint8Array(binary.length)
|
|
||||||
for (let i = 0; i < binary.length; i++) {
|
|
||||||
array[i] = binary.charCodeAt(i)
|
|
||||||
}
|
|
||||||
return new Blob([array], {type: mimeType})
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum GetMetadataError {
|
export enum GetMetadataError {
|
||||||
FileTooLarge = 'FileTooLarge',
|
FileTooLarge = 'FileTooLarge',
|
||||||
UnknownExtension = 'UnknownExtension',
|
UnknownExtension = 'UnknownExtension',
|
||||||
@@ -244,17 +234,31 @@ async function getMetadata(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const blob = dataURItoBlob(uri, mime)
|
const [, data] = uri.split(',')
|
||||||
if (blob.size > VIDEO_MAX_SIZE) {
|
const size = (data.length * 3) / 4
|
||||||
|
if (size > VIDEO_MAX_SIZE) {
|
||||||
return {
|
return {
|
||||||
error: GetMetadataError.FileTooLarge,
|
error: GetMetadataError.FileTooLarge,
|
||||||
asset: undefined,
|
asset: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = new File([blob], `tmp.${ext}`, {
|
const binary = atob(data)
|
||||||
|
if (binary.length > VIDEO_MAX_SIZE) {
|
||||||
|
return {
|
||||||
|
error: GetMetadataError.FileTooLarge,
|
||||||
|
asset: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const array = new Uint8Array(binary.length)
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
array[i] = binary.charCodeAt(i)
|
||||||
|
}
|
||||||
|
const file = new File([array], `tmp.${ext}`, {
|
||||||
type: mime,
|
type: mime,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return {
|
return {
|
||||||
error: GetMetadataError.FileCreationFailure,
|
error: GetMetadataError.FileCreationFailure,
|
||||||
@@ -312,6 +316,31 @@ async function processImagePickerAssets(
|
|||||||
let supportedAssets: ValidatedImagePickerAsset[] = []
|
let supportedAssets: ValidatedImagePickerAsset[] = []
|
||||||
|
|
||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
|
/*
|
||||||
|
* Some browsers won't even load massive files e.g. Arc
|
||||||
|
*/
|
||||||
|
if (!asset.uri) {
|
||||||
|
errors.add(SelectedAssetError.FileTooBig)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the file _did_ load, great, we can just check the size here.
|
||||||
|
*/
|
||||||
|
if (asset.file && asset.file.size > VIDEO_MAX_SIZE) {
|
||||||
|
errors.add(SelectedAssetError.FileTooBig)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In other cases e.g. Safari, we don't even get a `file`, so we want to
|
||||||
|
* approximate the size and fail early if we can.
|
||||||
|
*/
|
||||||
|
if (getDataUriSize(asset.uri) > VIDEO_MAX_SIZE) {
|
||||||
|
errors.add(SelectedAssetError.FileTooBig)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
const {success, type, mimeType} = classifyImagePickerAsset(asset)
|
const {success, type, mimeType} = classifyImagePickerAsset(asset)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -447,12 +476,6 @@ export function SelectMediaButton({
|
|||||||
|
|
||||||
const processSelectedAssets = useCallback(
|
const processSelectedAssets = useCallback(
|
||||||
async (rawAssets: ImagePickerAsset[]) => {
|
async (rawAssets: ImagePickerAsset[]) => {
|
||||||
// const {uri, ...rest} = rawAssets[0] || {}
|
|
||||||
// alert(JSON.stringify({
|
|
||||||
// uri: uri.slice(0, 40),
|
|
||||||
// ...rest
|
|
||||||
// }, null, 2))
|
|
||||||
// return
|
|
||||||
const {
|
const {
|
||||||
type,
|
type,
|
||||||
assets,
|
assets,
|
||||||
@@ -538,18 +561,13 @@ export function SelectMediaButton({
|
|||||||
selectionLimit: isIOS ? selectionCountRemaining : undefined,
|
selectionLimit: isIOS ? selectionCountRemaining : undefined,
|
||||||
preferredAssetRepresentationMode:
|
preferredAssetRepresentationMode:
|
||||||
UIImagePickerPreferredAssetRepresentationMode.Current,
|
UIImagePickerPreferredAssetRepresentationMode.Current,
|
||||||
|
videoMaxDuration: VIDEO_MAX_DURATION_MS / 1000,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (canceled) return
|
if (canceled) return
|
||||||
|
|
||||||
await processSelectedAssets(assets)
|
await processSelectedAssets(assets)
|
||||||
// if (isNative) {
|
|
||||||
// } else if (isWeb) {
|
|
||||||
// const {assets, canceled} = await pickVideo()
|
|
||||||
|
|
||||||
// await processSelectedAssets(assets)
|
|
||||||
// }
|
|
||||||
}, [
|
}, [
|
||||||
_,
|
_,
|
||||||
requestPhotoAccessIfNeeded,
|
requestPhotoAccessIfNeeded,
|
||||||
|
|||||||
Reference in New Issue
Block a user