I want to save an image with some metadata changes in a temp folder, without re-encoding the actual image data.
我想在临时文件夹中保存一些元数据更改的图像,而不重新编码实际的图像数据。
The only method that I found able to do this is ALAssetsLibrary/writeImageDataToSavedPhotosAlbum:metadata:completionBlock:, however, this one saves the image to the Photo Library. Instead, I want to save the image to a temp folder (for instance to share it by email, without populating the Photo Library).
我发现能够做到这一点的唯一方法是ALAssetsLibrary / writeImageDataToSavedPhotosAlbum:metadata:completionBlock:,但是,这个将图像保存到照片库。相反,我想将图像保存到临时文件夹(例如,通过电子邮件共享它,而不填充照片库)。
I've tried using CGImageDestinationRef
(CGImageDestinationAddImageFromSource), but it can only be created using a decoded image, which means it's re-encoding it when saved (tested, pixel bytes look different).
我尝试过使用CGImageDestinationRef(CGImageDestinationAddImageFromSource),但它只能使用解码图像创建,这意味着它在保存时会重新编码(测试时,像素字节看起来不同)。
Are there any other methods/classes available for iOS that can save image data along with metadata, besides using CGImageDestinationRef
? Suggestions for workarounds would also be welcomed.
除了使用CGImageDestinationRef之外,还有其他可用于iOS的方法/类可以保存图像数据和元数据吗?对于变通方法的建议也会受到欢迎。
3 个解决方案
#1
12
This is a frustrating problem with the iOS SDK. First, I recommend filing an enhancement request.
这是iOS SDK令人沮丧的问题。首先,我建议提交增强请求。
Now, here is a potential workaround: IF the ALAsset was created by you (i.e., its editable
property is YES
) then you can basically read the data, write with metadata, read again, save to disk, and then write with the original metadata.
现在,这是一个潜在的解决方法:如果您创建了ALAsset(即,其可编辑属性为YES),那么您基本上可以读取数据,使用元数据进行写入,再次读取,保存到磁盘,然后使用原始元数据进行写入。
This approach will avoid creating a duplicate image.
此方法将避免创建重复的图像。
Please read the // comments as I skipped some stuff for brevity (like building a metadata dictionary):
请阅读//注释,因为我为了简洁起见省略了一些内容(比如构建元数据字典):
ALAsset* asset; //get your asset, don't use this empty one
if (asset.editable) {
// get the source data
ALAssetRepresentation *rep = [asset defaultRepresentation];
Byte *buffer = (Byte*)malloc(rep.size);
// add error checking here
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
NSData *sourceData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
// make your metadata whatever you want
// you should use actual metadata, not a blank dictionary
NSDictionary *metadataDictionary = [NSDictionary dictionary];
// these are __weak to avoid creating an ARC retain cycle
NSData __weak *originalData = sourceData;
NSDictionary __weak *originalMetadata = [rep metadata];
[asset setImageData:sourceData
metadata:metadataDictionary
completionBlock:^(NSURL *assetURL, NSError *error) {
//now get your data and write it to file
if (!error) {
//get your data...
NSString *assetPath = [assetURL path];
NSData *targetData = [[NSFileManager defaultManager] contentsAtPath:assetPath];
//...write to file...
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [searchPaths lastObject];
NSURL *fileURL = [NSURL fileURLWithPath:documentPath];
[targetData writeToURL:fileURL atomically:YES];
//...and put it back the way it was
[asset setImageData:originalData metadata:originalMetadata completionBlock:nil];
} else {
// handle error on setting data
NSLog(@"ERROR: %@", [error localizedDescription]);
}
}];
} else {
// you'll need to make a new ALAsset which you have permission to edit and then try again
}
As you can see, if the ALAsset isn't owned by you, you will need to create one, which will add a photo to the user's library, which is exactly what you wanted to avoid. However, as you may have guessed, you cannot delete an ALAsset from the user's photo library, even if your app created it. (Feel free to file another enhancement request for that.)
如您所见,如果您不拥有ALAsset,则需要创建一个,这会将照片添加到用户的库中,这正是您想要避免的。但是,正如您可能已经猜到的那样,即使您的应用程序创建了ALAsset,也无法从用户的照片库中删除ALAsset。 (随意提交另一个增强请求。)
So, if the photo/image was created in your app, this will work for you.
因此,如果照片/图片是在您的应用中创建的,那么这对您有用。
But if not, it will create an additional copy the user must delete.
但如果没有,它将创建用户必须删除的附加副本。
The only alternative is to parse the NSData yourself, which would be a pain. There is no open-source library that I am aware of to fill this gap in the iOS SDK.
唯一的选择是自己解析NSData,这将是一个痛苦。我知道没有开源库可以填补iOS SDK中的这个空白。
#2
2
Alongside iphone-exif, and Aaron's answer, you might also want to look at the libexif c library.
除了iphone-exif和Aaron的答案之外,您可能还想查看libexif c库。
see also HCO23's answer to
How to write or modify EXIF data for an existing image on the filesystem, without loading the image?
另请参阅HCO23的答案如何在不加载图像的情况下为文件系统上的现有图像编写或修改EXIF数据?
(@HCO23 has used libexif in iOS projects).
(@ HCO23在iOS项目中使用了libexif)。
It's also worth noting that many of the 'professional' metadata editing apps in the app store seem to skirt around the issue by creating sidecar/xmp files.
值得注意的是,应用程序商店中的许多“专业”元数据编辑应用程序似乎都是通过创建sidecar / xmp文件来解决这个问题。
#3
2
Try This :
尝试这个 :
- (void)saveImage: (UIImage*)image
{
if (image != nil)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
@"test.png" ];
NSData* data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
}
}
- (UIImage*)loadImage
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
@"test.png" ];
UIImage* image = [UIImage imageWithContentsOfFile:path];
[self sendAction:path];
return image;
}
#1
12
This is a frustrating problem with the iOS SDK. First, I recommend filing an enhancement request.
这是iOS SDK令人沮丧的问题。首先,我建议提交增强请求。
Now, here is a potential workaround: IF the ALAsset was created by you (i.e., its editable
property is YES
) then you can basically read the data, write with metadata, read again, save to disk, and then write with the original metadata.
现在,这是一个潜在的解决方法:如果您创建了ALAsset(即,其可编辑属性为YES),那么您基本上可以读取数据,使用元数据进行写入,再次读取,保存到磁盘,然后使用原始元数据进行写入。
This approach will avoid creating a duplicate image.
此方法将避免创建重复的图像。
Please read the // comments as I skipped some stuff for brevity (like building a metadata dictionary):
请阅读//注释,因为我为了简洁起见省略了一些内容(比如构建元数据字典):
ALAsset* asset; //get your asset, don't use this empty one
if (asset.editable) {
// get the source data
ALAssetRepresentation *rep = [asset defaultRepresentation];
Byte *buffer = (Byte*)malloc(rep.size);
// add error checking here
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
NSData *sourceData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
// make your metadata whatever you want
// you should use actual metadata, not a blank dictionary
NSDictionary *metadataDictionary = [NSDictionary dictionary];
// these are __weak to avoid creating an ARC retain cycle
NSData __weak *originalData = sourceData;
NSDictionary __weak *originalMetadata = [rep metadata];
[asset setImageData:sourceData
metadata:metadataDictionary
completionBlock:^(NSURL *assetURL, NSError *error) {
//now get your data and write it to file
if (!error) {
//get your data...
NSString *assetPath = [assetURL path];
NSData *targetData = [[NSFileManager defaultManager] contentsAtPath:assetPath];
//...write to file...
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [searchPaths lastObject];
NSURL *fileURL = [NSURL fileURLWithPath:documentPath];
[targetData writeToURL:fileURL atomically:YES];
//...and put it back the way it was
[asset setImageData:originalData metadata:originalMetadata completionBlock:nil];
} else {
// handle error on setting data
NSLog(@"ERROR: %@", [error localizedDescription]);
}
}];
} else {
// you'll need to make a new ALAsset which you have permission to edit and then try again
}
As you can see, if the ALAsset isn't owned by you, you will need to create one, which will add a photo to the user's library, which is exactly what you wanted to avoid. However, as you may have guessed, you cannot delete an ALAsset from the user's photo library, even if your app created it. (Feel free to file another enhancement request for that.)
如您所见,如果您不拥有ALAsset,则需要创建一个,这会将照片添加到用户的库中,这正是您想要避免的。但是,正如您可能已经猜到的那样,即使您的应用程序创建了ALAsset,也无法从用户的照片库中删除ALAsset。 (随意提交另一个增强请求。)
So, if the photo/image was created in your app, this will work for you.
因此,如果照片/图片是在您的应用中创建的,那么这对您有用。
But if not, it will create an additional copy the user must delete.
但如果没有,它将创建用户必须删除的附加副本。
The only alternative is to parse the NSData yourself, which would be a pain. There is no open-source library that I am aware of to fill this gap in the iOS SDK.
唯一的选择是自己解析NSData,这将是一个痛苦。我知道没有开源库可以填补iOS SDK中的这个空白。
#2
2
Alongside iphone-exif, and Aaron's answer, you might also want to look at the libexif c library.
除了iphone-exif和Aaron的答案之外,您可能还想查看libexif c库。
see also HCO23's answer to
How to write or modify EXIF data for an existing image on the filesystem, without loading the image?
另请参阅HCO23的答案如何在不加载图像的情况下为文件系统上的现有图像编写或修改EXIF数据?
(@HCO23 has used libexif in iOS projects).
(@ HCO23在iOS项目中使用了libexif)。
It's also worth noting that many of the 'professional' metadata editing apps in the app store seem to skirt around the issue by creating sidecar/xmp files.
值得注意的是,应用程序商店中的许多“专业”元数据编辑应用程序似乎都是通过创建sidecar / xmp文件来解决这个问题。
#3
2
Try This :
尝试这个 :
- (void)saveImage: (UIImage*)image
{
if (image != nil)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
@"test.png" ];
NSData* data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
}
}
- (UIImage*)loadImage
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
@"test.png" ];
UIImage* image = [UIImage imageWithContentsOfFile:path];
[self sendAction:path];
return image;
}