Cleanup files after each iteration of compression and downloading (#3599)
* delete image on each iteration of compression * replace a few other instances of `unlink()` * ensure that moving to the permanent path will succeed * use `cacheDirectory` * missing file extension? * assert * Remove extra . * Extract safeDeleteAsync, fix normalization * Normalize everywhere * Use safeDeleteAsync in more places * Delete .bin too --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>zio/stable
parent
90c3ec8749
commit
c3fcd486b3
|
@ -1,4 +1,3 @@
|
||||||
import {deleteAsync} from 'expo-file-system'
|
|
||||||
import {
|
import {
|
||||||
AppBskyEmbedExternal,
|
AppBskyEmbedExternal,
|
||||||
AppBskyEmbedImages,
|
AppBskyEmbedImages,
|
||||||
|
@ -20,6 +19,7 @@ import {shortenLinks} from 'lib/strings/rich-text-manip'
|
||||||
import {isNative, isWeb} from 'platform/detection'
|
import {isNative, isWeb} from 'platform/detection'
|
||||||
import {ImageModel} from 'state/models/media/image'
|
import {ImageModel} from 'state/models/media/image'
|
||||||
import {LinkMeta} from '../link-meta/link-meta'
|
import {LinkMeta} from '../link-meta/link-meta'
|
||||||
|
import {safeDeleteAsync} from '../media/manip'
|
||||||
|
|
||||||
export interface ExternalEmbedDraft {
|
export interface ExternalEmbedDraft {
|
||||||
uri: string
|
uri: string
|
||||||
|
@ -119,15 +119,9 @@ export async function post(agent: BskyAgent, opts: PostOpts) {
|
||||||
const {width, height} = image.compressed || image
|
const {width, height} = image.compressed || image
|
||||||
logger.debug(`Uploading image`)
|
logger.debug(`Uploading image`)
|
||||||
const res = await uploadBlob(agent, path, 'image/jpeg')
|
const res = await uploadBlob(agent, path, 'image/jpeg')
|
||||||
|
|
||||||
if (isNative) {
|
if (isNative) {
|
||||||
try {
|
safeDeleteAsync(path)
|
||||||
deleteAsync(path)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
images.push({
|
images.push({
|
||||||
image: res.data.blob,
|
image: res.data.blob,
|
||||||
alt: image.altText ?? '',
|
alt: image.altText ?? '',
|
||||||
|
@ -182,13 +176,8 @@ export async function post(agent: BskyAgent, opts: PostOpts) {
|
||||||
encoding,
|
encoding,
|
||||||
)
|
)
|
||||||
thumb = thumbUploadRes.data.blob
|
thumb = thumbUploadRes.data.blob
|
||||||
|
|
||||||
try {
|
|
||||||
if (isNative) {
|
if (isNative) {
|
||||||
deleteAsync(opts.extLink.localThumb.path)
|
safeDeleteAsync(opts.extLink.localThumb.path)
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {Image as RNImage, Share as RNShare} from 'react-native'
|
import {Image as RNImage, Share as RNShare} from 'react-native'
|
||||||
import * as RNFS from 'react-native-fs'
|
|
||||||
import {Image} from 'react-native-image-crop-picker'
|
import {Image} from 'react-native-image-crop-picker'
|
||||||
import uuid from 'react-native-uuid'
|
import uuid from 'react-native-uuid'
|
||||||
|
import {cacheDirectory, copyAsync, deleteAsync} from 'expo-file-system'
|
||||||
import * as MediaLibrary from 'expo-media-library'
|
import * as MediaLibrary from 'expo-media-library'
|
||||||
import * as Sharing from 'expo-sharing'
|
import * as Sharing from 'expo-sharing'
|
||||||
import ImageResizer from '@bam.tech/react-native-image-resizer'
|
import ImageResizer from '@bam.tech/react-native-image-resizer'
|
||||||
|
@ -24,7 +24,10 @@ export async function compressIfNeeded(
|
||||||
mode: 'stretch',
|
mode: 'stretch',
|
||||||
maxSize,
|
maxSize,
|
||||||
})
|
})
|
||||||
const finalImageMovedPath = await moveToPermanentPath(resizedImage.path)
|
const finalImageMovedPath = await moveToPermanentPath(
|
||||||
|
resizedImage.path,
|
||||||
|
'.jpg',
|
||||||
|
)
|
||||||
const finalImg = {
|
const finalImg = {
|
||||||
...resizedImage,
|
...resizedImage,
|
||||||
path: finalImageMovedPath,
|
path: finalImageMovedPath,
|
||||||
|
@ -69,13 +72,10 @@ export async function downloadAndResize(opts: DownloadAndResizeOpts) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let localUri = downloadRes.path()
|
const localUri = normalizePath(downloadRes.path(), true)
|
||||||
if (!localUri.startsWith('file://')) {
|
|
||||||
localUri = `file://${localUri}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return await doResize(localUri, opts)
|
return await doResize(localUri, opts)
|
||||||
} finally {
|
} finally {
|
||||||
|
// TODO Whenever we remove `rn-fetch-blob`, we will need to replace this `flush()` with a `deleteAsync()` -hailey
|
||||||
if (downloadRes) {
|
if (downloadRes) {
|
||||||
downloadRes.flush()
|
downloadRes.flush()
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,8 @@ export async function shareImageModal({uri}: {uri: string}) {
|
||||||
UTI: 'image/png',
|
UTI: 'image/png',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
RNFS.unlink(imagePath)
|
|
||||||
|
safeDeleteAsync(imagePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveImageToMediaLibrary({uri}: {uri: string}) {
|
export async function saveImageToMediaLibrary({uri}: {uri: string}) {
|
||||||
|
@ -128,6 +129,7 @@ export async function saveImageToMediaLibrary({uri}: {uri: string}) {
|
||||||
|
|
||||||
// save
|
// save
|
||||||
await MediaLibrary.createAssetAsync(imagePath)
|
await MediaLibrary.createAssetAsync(imagePath)
|
||||||
|
safeDeleteAsync(imagePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getImageDim(path: string): Promise<Dimensions> {
|
export function getImageDim(path: string): Promise<Dimensions> {
|
||||||
|
@ -174,6 +176,8 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
|
||||||
width: resizeRes.width,
|
width: resizeRes.width,
|
||||||
height: resizeRes.height,
|
height: resizeRes.height,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
safeDeleteAsync(resizeRes.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -181,7 +185,7 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function moveToPermanentPath(path: string, ext = ''): Promise<string> {
|
async function moveToPermanentPath(path: string, ext = 'jpg'): Promise<string> {
|
||||||
/*
|
/*
|
||||||
Since this package stores images in a temp directory, we need to move the file to a permanent location.
|
Since this package stores images in a temp directory, we need to move the file to a permanent location.
|
||||||
Relevant: IOS bug when trying to open a second time:
|
Relevant: IOS bug when trying to open a second time:
|
||||||
|
@ -189,14 +193,33 @@ async function moveToPermanentPath(path: string, ext = ''): Promise<string> {
|
||||||
*/
|
*/
|
||||||
const filename = uuid.v4()
|
const filename = uuid.v4()
|
||||||
|
|
||||||
const destinationPath = joinPath(
|
// cacheDirectory will not ever be null on native, but it could be on web. This function only ever gets called on
|
||||||
RNFS.TemporaryDirectoryPath,
|
// native so we assert as a string.
|
||||||
`${filename}${ext}`,
|
const destinationPath = joinPath(cacheDirectory as string, filename + ext)
|
||||||
)
|
await copyAsync({
|
||||||
await RNFS.moveFile(path, destinationPath)
|
from: normalizePath(path),
|
||||||
|
to: normalizePath(destinationPath),
|
||||||
|
})
|
||||||
|
safeDeleteAsync(path)
|
||||||
return normalizePath(destinationPath)
|
return normalizePath(destinationPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function safeDeleteAsync(path: string) {
|
||||||
|
// Normalize is necessary for Android, otherwise it doesn't delete.
|
||||||
|
const normalizedPath = normalizePath(path)
|
||||||
|
try {
|
||||||
|
await Promise.allSettled([
|
||||||
|
deleteAsync(normalizedPath, {idempotent: true}),
|
||||||
|
// HACK: Try this one too. Might exist due to api-polyfill hack.
|
||||||
|
deleteAsync(normalizedPath.replace(/\.jpe?g$/, '.bin'), {
|
||||||
|
idempotent: true,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to delete file', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function joinPath(a: string, b: string) {
|
function joinPath(a: string, b: string) {
|
||||||
if (a.endsWith('/')) {
|
if (a.endsWith('/')) {
|
||||||
if (b.startsWith('/')) {
|
if (b.startsWith('/')) {
|
||||||
|
|
Loading…
Reference in New Issue