I'm reading a file from the file system on macOS, I tried to extract the metadata with the following code:
let imagedata = try? Data(contentsOf: fileUrl)
if imagedata == nil {
print("Could not read contents of image")
return
}
var source: CGImageSource = CGImageSourceCreateWithData((imagedata as! CFMutableData), nil)!
var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [AnyHashable: Any]
print("image metadata", metadata)
And this is the output that I get:
image metadata Optional([AnyHashable("ColorModel"): RGB, AnyHashable("DPIHeight"): 72, AnyHashable("PixelHeight"): 840, AnyHashable("ProfileName"): sRGB IEC61966-2.1, AnyHashable("PixelWidth"): 840, AnyHashable("{PNG}"): {
Chromaticities = (
"0.3127",
"0.329",
"0.64",
"0.33",
"0.3",
"0.6000000000000001",
"0.15",
"0.06"
);
Gamma = "0.45455";
InterlaceType = 0;
XPixelsPerMeter = 2834;
YPixelsPerMeter = 2834;
sRGBIntent = 0;
}, AnyHashable("DPIWidth"): 72, AnyHashable("Depth"): 8])
And that extracts the usual metadata, but I actually would like to know where the image was downloaded from, when I drag n' drop an image into the desktop and then right click and then get info
, I see the field:
Is there any way to retrieve this field?
Edit: I tried this:
if let attr = try? FileManager.default.attributesOfItem(atPath: fileUrl.path) {
print("attributes", attr)
}
and now I can see the kMDItemWhereFroms
field, but I don't know how to reach it or parse. Maybe someone has an idea here?
I think it's always nice to show how tinker, how to find the data, so I'll show how I got it, while I didn't know at all about it.
First thought, it might not be in image Metadata, but in "File System" ones, so let's look at FileManager. It's in FileManager.attributesOfItem(atPath:)
Step 1:
let attributes = try fileManager.attributesOfItem(atPath: filePath)
This gives me:
[__C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountName): staff,
__C.NSFileAttributeKey(_rawValue: NSFileReferenceCount): 1,
__C.NSFileAttributeKey(_rawValue: NSFileModificationDate): 2020-05-13 15:16:20 +0000,
__C.NSFileAttributeKey(_rawValue: NSFileSystemFileNumber): 8635383780,
__C.NSFileAttributeKey(_rawValue: NSFilePosixPermissions): 420,
__C.NSFileAttributeKey(_rawValue: NSFileType): NSFileTypeRegular,
__C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountID): 501,
__C.NSFileAttributeKey(_rawValue: NSFileGroupOwnerAccountID): 20,
__C.NSFileAttributeKey(_rawValue: NSFileExtensionHidden): 0,
__C.NSFileAttributeKey(_rawValue: NSFileHFSTypeCode): 0,
__C.NSFileAttributeKey(_rawValue: NSFileCreationDate): 2020-05-13 15:16:20 +0000,
__C.NSFileAttributeKey(_rawValue: NSFileOwnerAccountName): Larme,
__C.NSFileAttributeKey(_rawValue: NSFileSize): 1258299,
__C.NSFileAttributeKey(_rawValue: NSFileSystemNumber): 16777220,
__C.NSFileAttributeKey(_rawValue: NSFileExtendedAttributes): {
"com.apple.lastuseddate#PS" = {length = 16, bytes = 0x440fbc5e00000000503f091700000000};
"com.apple.macl" = {length = 72, bytes = 0x02009685 15175d5d 47089537 71b6a786 ... 00000000 00000000 };
"com.apple.metadata:kMDItemDownloadedDate" = {length = 53, bytes = 0x62706c69 73743030 a1013341 c2362362 ... 00000000 00000013 };
"com.apple.metadata:kMDItemWhereFroms" = {length = 414, bytes = 0x62706c69 73743030 a201025f 11013b68 ... 00000000 00000178 };
"com.apple.quarantine" = {length = 57, bytes = 0x30303833 3b356562 63306634 343b5361 ... 44344445 45343845 };
}, __C.NSFileAttributeKey(_rawValue: NSFileHFSCreatorCode): 0]
What to look in this info:
[...
__C.NSFileAttributeKey(_rawValue: NSFileExtendedAttributes): {
...
"com.apple.metadata:kMDItemWhereFroms" = {length = 414, bytes = 0x62706c69 73743030 a201025f 11013b68 ... 00000000 00000178 };
...
]
Ok, so let's continue to step 2, let's dig deeper:
let extendedAttributes = attributes[FileAttributeKey(rawValue: "NSFileExtendedAttributes")] as! [String: Any]
let data = extendedAttributes["com.apple.metadata:kMDItemWhereFroms"] as! Data //that's the new output for `Data` in new systems (iOS13+, etc.)
As always, I tend to do just in case to get more info: let str = String(data: data, encoding: .utf8)
which returns nil. Let's remember that not all data is convertible into utf8 string like that. For instance, an image won't make it.
But I know a few tricks, let's do instead: let str = String(data: data, encoding: .ascii)
, this gives me:
"bplist00¢\u{01}\u{02}_\u{11}\u{01};https://doc-04-6k-docs.googleusercontent.com/docs/securesc/teo5l5s1g996hre86q7qv786r7me56c3/3nneqai4sh4lvk3bdlnpokt8793t4t5t/1589382975000/11847256646728493158/11847256646728493158/0B1U9OypmOvxpODVFb2VGZEdpckk?e=download&authuser=0&nonce=faav7fnl402re&user=11847256646728493158&hash=vl51p8m313rnnao3dleqsl348rp2vo82_\u{10}+https://drive.google.com/drive/u/0/my-drive\0\u{08}\0\u{0B}\u{01}J\0\0\0\0\0\0\u{02}\u{01}\0\0\0\0\0\0\0\u{03}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\u{01}x"
I downloaded my sample from my Google Drive, info seems making sense. What do see there?: bplist
, that's the most important one here.
A PropertyList? Let's decode it then:
let urls = try PropertyListDecoder().decode([String].self, from: data)
It made sense for me to be a [String]
, it would have been different, that would had be harder. But it's a simple one.
Final code:
static func downloadedFromURLs(for filePath: String) -> [String]? {
let fileManager = FileManager.default
guard fileManager.fileExists(atPath: filePath) else { print("File doesn't exists"); return nil }
do {
let attributes = try fileManager.attributesOfItem(atPath: filePath)
guard let extendedAttributes = attributes[FileAttributeKey(rawValue: "NSFileExtendedAttributes")] as? [String: Any] else {
print("Didn't find NSFileExtendedAttributes value")
return nil
}
guard let data = extendedAttributes["com.apple.metadata:kMDItemWhereFroms"] as? Data else {
print("Didn't find com.apple.metadata:kMDItemWhereFroms value")
return nil
}
let urls = try PropertyListDecoder().decode([String].self, from: data)
return urls
} catch {
print("Error: \(error)")
return nil
}
}
Side note:
There might be some constant instead of writing hard coded strings for "com.apple.metadata:kMDItemWhereFroms"
and NSFileExtendedAttributes
, but I didn't see it here.
Some related interested discussion. https://apple.stackexchange.com/questions/110239/where-is-the-where-from-meta-data-stored-when-downloaded-via-chrome