Some cleanup regarding temporary file handling

- Renamed `createTmpVideo`to `copyFileToTemp` and keep the method generic, since it has no special code for video files
- Renamed `tempFilePath` to `tempFilePathForExtension`, since it creates unique path only for a file extension
- Document `tempFilePathForExtension`
- Moved `cleanup`, `tempFilePath` and `copyFileToTemp` at one place, since they are related
This commit is contained in:
Manuel Beck
2026-01-21 13:52:17 +01:00
parent 672e09cd87
commit 13811daa3c

View File

@@ -361,7 +361,7 @@ static NSString* MIME_JPEG = @"image/jpeg";
}
// Copy video to a temporary location, so it can be accessed after this completion handler returns
NSString* tempVideoPath = [weakSelf createTmpVideo:[url path]];
NSString* tempVideoPath = [weakSelf copyFileToTemp:[url path]];
// Send Cordova plugin result back, which must be done on the main thread
CDVPluginResult* result = nil;
@@ -502,41 +502,6 @@ static NSString* MIME_JPEG = @"image/jpeg";
}
}
- (void)cleanup:(CDVInvokedUrlCommand*)command
{
// empty the tmp directory
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError* err = nil;
BOOL hasErrors = NO;
// clear contents of NSTemporaryDirectory
NSString* tempDirectoryPath = NSTemporaryDirectory();
NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
NSString* fileName = nil;
BOOL result;
while ((fileName = [directoryEnumerator nextObject])) {
// only delete the files we created
if (![fileName hasPrefix:CDV_PHOTO_PREFIX]) {
continue;
}
NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
result = [fileMgr removeItemAtPath:filePath error:&err];
if (!result && err) {
NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
hasErrors = YES;
}
}
CDVPluginResult* pluginResult;
if (hasErrors) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:@"One or more files failed to be deleted."];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (NSString*)getMimeForEncoding:(CDVEncodingType)encoding
{
switch (encoding) {
@@ -726,17 +691,6 @@ static NSString* MIME_JPEG = @"image/jpeg";
}
- (NSString*)tempFilePath:(NSString*)extension
{
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
// unique file name
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
NSNumber *timeStampObj = [NSNumber numberWithDouble: timeStamp];
NSString* filePath = [NSString stringWithFormat:@"%@/%@%ld.%@", docsPath, CDV_PHOTO_PREFIX, [timeStampObj longValue], extension];
return filePath;
}
- (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)options
{
// get the image
@@ -807,7 +761,7 @@ static NSString* MIME_JPEG = @"image/jpeg";
NSError* err = nil;
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
NSString* filePath = [self tempFilePath:extension];
NSString* filePath = [self tempFilePathForExtension:extension];
// save file
if (![imageDataWithExif writeToFile:filePath options:NSAtomicWrite error:&err]) {
@@ -822,7 +776,7 @@ static NSString* MIME_JPEG = @"image/jpeg";
} else if (pickerController.sourceType != UIImagePickerControllerSourceTypeCamera || !options.usesGeolocation) {
// No need to save file if usesGeolocation is true since it will be saved after the location is tracked
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
NSString* filePath = [self tempFilePath:extension];
NSString* filePath = [self tempFilePathForExtension:extension];
NSError* err = nil;
// save file
@@ -853,33 +807,113 @@ static NSString* MIME_JPEG = @"image/jpeg";
// On iOS 13 the movie path becomes inaccessible, create and return a copy
if (IsAtLeastiOSVersion(@"13.0")) {
moviePath = [self createTmpVideo:[[info objectForKey:UIImagePickerControllerMediaURL] path]];
moviePath = [self copyFileToTemp:[[info objectForKey:UIImagePickerControllerMediaURL] path]];
}
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath];
}
- (NSString*)createTmpVideo:(NSString*)moviePath
/**
Generates a unique temporary file path for a file extension.
The filename is prefixed with `cdv_photo_` and suffixed with the provided
file extension. A UNIX timestamp (seconds since 1970) is used to ensure
uniqueness between calls.
Threading: Safe to call from any thread. Uses NSTemporaryDirectory() and
does not perform any I/O; it only constructs a path string.
@param fileExtension The desired file extension without a leading dot
(for example, "jpg", "png", or the original video
extension like "mov").
@return An absolute path string within the app's temporary directory,
e.g. `/var/mobile/Containers/Data/Application/<UUID>/tmp/cdv_photo_<timestamp>.jpg`.
@discussion The returned path is not created on disk. Callers are responsible
for writing data to the path and handling any errors.
@note Only files whose names start with `cdv_photo_` are cleaned up by the
plugin's `cleanup:` method.
**/
- (NSString*)tempFilePathForExtension:(NSString*)fileExtension
{
NSString* copyMoviePath = [self tempFilePath:[moviePath pathExtension]];
NSFileManager* fileMgr = [[NSFileManager alloc] init];
// Return a unique file name like
// `/var/mobile/Containers/Data/Application/<UUID>/tmp/cdv_photo_<timestamp>.jpg`.
return [NSString stringWithFormat:
@"%@/%@%lld.%@",
[NSTemporaryDirectory() stringByStandardizingPath],
CDV_PHOTO_PREFIX,
(long long)[[NSDate date] timeIntervalSince1970],
fileExtension];
}
- (NSString*)copyFileToTemp:(NSString*)filePath
{
NSFileManager* fileManager = [[NSFileManager alloc] init];
NSString* tempFilePath = [self tempFilePathForExtension:[filePath pathExtension]];
NSError *error = nil;
// Copy the video file to the temp directory
BOOL copySuccess = [fileMgr copyItemAtPath:moviePath toPath:copyMoviePath error:&error];
// Copy file to temp directory
BOOL copySuccess = [fileManager copyItemAtPath:filePath toPath:tempFilePath error:&error];
if (!copySuccess || error) {
NSLog(@"CDVCamera: Failed to copy video file from %@ to %@. Error: %@", moviePath, copyMoviePath, [error localizedDescription]);
NSLog(@"CDVCamera: Failed to copy file from %@ to temporary path %@. Error: %@", filePath, tempFilePath, [error localizedDescription]);
return nil;
}
// Verify the copied file exists
if (![fileMgr fileExistsAtPath:copyMoviePath]) {
NSLog(@"CDVCamera: Copied video file does not exist at path: %@", copyMoviePath);
if (![fileManager fileExistsAtPath:tempFilePath]) {
NSLog(@"CDVCamera: Copied file does not exist at temporary path: %@", tempFilePath);
return nil;
}
return [[NSURL fileURLWithPath:copyMoviePath] absoluteString];
return [[NSURL fileURLWithPath:tempFilePath] absoluteString];
}
/**
Called by JS camera.cleanup()
Removes intermediate image files that are kept in temporary storage after
calling camera.getPicture.
*/
- (void)cleanup:(CDVInvokedUrlCommand*)command
{
// empty the tmp directory
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError* err = nil;
BOOL hasErrors = NO;
// clear contents of NSTemporaryDirectory
NSString* tempDirectoryPath = NSTemporaryDirectory();
NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
NSString* fileName = nil;
BOOL result;
while ((fileName = [directoryEnumerator nextObject])) {
// only delete the files we created
if (![fileName hasPrefix:CDV_PHOTO_PREFIX]) {
continue;
}
NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
result = [fileMgr removeItemAtPath:filePath error:&err];
if (!result && err) {
NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
hasErrors = YES;
}
}
CDVPluginResult* pluginResult;
if (hasErrors) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:@"One or more files failed to be deleted."];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
#pragma mark UIImagePickerControllerDelegate methods
@@ -1072,7 +1106,7 @@ static NSString* MIME_JPEG = @"image/jpeg";
{
NSError* err = nil;
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
NSString* filePath = [self tempFilePath:extension];
NSString* filePath = [self tempFilePathForExtension:extension];
// save file
if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) {