Is there a way to get the file size on disk of a PHAsset
without doing requestImageDataForAsset
or converting it to ALAsset
? I am loading in previews of a user's photo library - potentially many thousands of them - and it's imperative to my app that they know the size before import.
Get file size of PHAsset without loading in the resource?
Asked Answered
You can grab the fileSize
of a PHAsset and convert it to human readable form like this:
let resources = PHAssetResource.assetResources(for: yourAsset) // your PHAsset
var sizeOnDisk: Int64? = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
}
Then use your sizeOnDisk
variable and pass it into a method like this...
func converByteToHumanReadable(_ bytes:Int64) -> String {
let formatter:ByteCountFormatter = ByteCountFormatter()
formatter.countStyle = .binary
return formatter.string(fromByteCount: Int64(bytes))
}
It's not in any documentation because it's accessing the header files. Technically this is a small piece of Apple's private APIs. However, we have submitted a build and received full approval without issue. –
Ovarian
Is it safety to take only first item? As apple said: " An asset can contain multiple resources - for example, an edited photo asset contains resources for both the original and edited images". –
Conformance
We'll probably need to check the
resources
for the image resources: kUTTypeJPEG, AVFileType.heic.rawValue, kUTTypePNG, kUTTypeTIFF, kUTTypeRawImage
–
Palomo UPD. But I noticed that the
resources.last
is always the most recent resource (iOS 13). –
Palomo For iCloud albums video and images, I'm getting zero (0) as size. –
Vandalize
SWIFT 5.0 Light & Easy:
private static let bcf = ByteCountFormatter()
func getSize(asset: PHAsset) -> String {
let resources = PHAssetResource.assetResources(for: asset)
guard let resource = resources.first,
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong else {
return "Unknown"
}
let sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64))
Self.bcf.allowedUnits = [.useMB]
Self.bcf.countStyle = .file
return Self.bcf.string(fromByteCount: sizeOnDisk)
}
A safer solution:
[asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
NSNumber *fileSize = nil;
NSError *error = nil;
[contentEditingInput.fullSizeImageURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:&error];
NSLog(@"file size: %@\nerror: %@", fileSize, error);
}];
Swift version:
asset.requestContentEditingInput(with: nil) { (contentEditingInput, _) in
do {
let fileSize = try contentEditingInput?.fullSizeImageURL?.resourceValues(forKeys: [URLResourceKey.fileSizeKey]).fileSize
print("file size: \(String(describing: fileSize))")
} catch let error {
fatalError("error: \(error)")
}
}
Inspired by How to get an ALAsset URL from a PHAsset?
This method is so heavy. Won't work for the thousands of assets. –
Palomo
Yes, so it is better to execute in a background thread and cache the result. –
Skinner
Please try this.
let resources = PHAssetResource.assetResources(for: YourAsset)
var sizeOnDisk: Int64 = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
totalSize.text = String(format: "%.2f", Double(sizeOnDisk) / (1024.0*1024.0))+" MB"
}
© 2022 - 2024 — McMap. All rights reserved.