用户可以从服务器下载图像和视频文件,然后将其临时存储在应用程序的文档路径下,然后使用
PHAssetChangeRequest.creationRequestForAssetFromImage
或PHAssetChangeRequest.creationRequestForAssetFromVideo
从那里复制到iOS照片库中的自定义相册。
当仅下载图像文件或视频文件时,此功能完美无缺,但当图像和视频混合在同一个下载批次中时,总会有一些文件因以下错误之一而任意失败:
错误是:操作无法完成。 (PHPhotosErrorDomain 错误 -1。)
或
错误是:操作无法完成。 (PHPhotosErrorDomain 错误 3302。)
这是将文件复制到照片库的负责代码:
public func saveFileToPhotoLibrary(_ url: URL, completion: @escaping (Bool) -> Void)
{
guard let album = _album else
{
completion(false)
return
}
guard let supertype = url.supertype else
{
completion(false)
return
}
if supertype == .image || supertype == .movie
{
DispatchQueue.global(qos: .background).async
{
PHPhotoLibrary.shared().performChanges(
{
guard let assetChangeRequest = supertype == .image
? PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: url)
: PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url) else
{
completion(false)
return
}
guard let assetPlaceholder = assetChangeRequest.placeholderForCreatedAsset else
{
completion(false)
return
}
guard let albumChangeRequest = PHAssetCollectionChangeRequest(for: album) else
{
completion(false)
return
}
albumChangeRequest.addAssets([assetPlaceholder] as NSFastEnumeration)
})
{
saved, error in
if let error = error
{
print("Failed to save image file from \(url.lastPathComponent) to photo gallery. Error was: \(error.localizedDescription)")
DispatchQueue.main.async { completion(false) }
}
}
}
}
}
有谁知道为什么会发生错误或如何解决此问题以免文件副本失败?
解决了!上面的代码按预期工作。问题在于,由于服务器提供的文件顺序和文件名,某些视频和图像文件之间的文件类型混淆了。因此,有时视频可能会收到图像文件类型扩展名,反之亦然。这似乎混淆了 PHPhotoLibrary 创建RequestForAssetFromImage API。
PHPhotosErrorDomain error 3302
表示 invalidResource
,请参阅错误代码列表。这发生在我身上,因为我试图从临时目录中的文件中保存图片。似乎只能使用特定目录:
// FAILS
let imageUrl = FileManager.default.temporaryDirectory.appendingPathComponent("save-me")
// WORKS
let documentsPath = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true)[0]
let filePath = "\(documentsPath)/tempFile.jpg"
let imageUrl = URL(fileURLWithPath: filePath)
// And then
PHPhotoLibrary.shared().performChanges(
{
let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(
atFileURL: imageUrl)
[...]
您还可能会收到其他神秘错误。例如,
3300
表示 changeNotSupported
,如果您尝试将资产添加到特殊相册(例如“Recents”)(标识为 PHAssetCollectionType.smartAlbum
+ PHAssetCollectionSubtype.smartAlbumUserLibrary
),则可能会出现此情况。
我也遇到了同样的错误,当我们下载并创建扩展名(MemeType)时,应该是一个正确的例子,如果我们想保存 mp4 文件,我们应该下载 .mp4 文件,如果任何原因不匹配,那么我们将面临这个类型错误。当您不确切知道文件类型时,我会安排代码片段来实现它。
enum FileType {
case jpg
case mp4
case ogv
case webm
case threeGP
case unknown
}
func determineFileType(data: Data) -> FileType {
var values = [UInt8](repeating:0, count:4)
data.copyBytes(to: &values, count: 4)
if values[0] == 0xFF && values[1] == 0xD8 && values[2] == 0xFF {
return .jpg
} else if values[0] == 0x00 && values[1] == 0x00 && values[2] == 0x00 {
return .mp4 // Basic heuristic for .mp4
} else if values[0] == 0x4F && values[1] == 0x67 && values[2] == 0x67 && values[3] == 0x53 {
return .ogv
} else if values[0] == 0x1A && values[1] == 0x45 && values[2] == 0xDF && values[3] == 0xA3 {
return .webm
} else if values[0] == 0x66 && values[1] == 0x74 && values[2] == 0x79 && values[3] == 0x70 {
return .threeGP // Basic heuristic for .3gp
} else {
return .unknown
}}
private func saveDataAsVideoFile(_ data: Data, completion: @escaping (Bool, Error?) -> Void) {
let fileType = determineFileType(data: data)
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filePath: String
switch fileType {
case .jpg:
filePath = "\(documentsPath)/tempFile.jpg"
case .mp4:
filePath = "\(documentsPath)/tempFile.mp4"
case .ogv:
filePath = "\(documentsPath)/tempFile.ogv"
case .webm:
filePath = "\(documentsPath)/tempFile.webm"
case .threeGP:
filePath = "\(documentsPath)/tempFile.3gp"
case .unknown:
completion(false, nil)
return
}
let fileUrl = URL(fileURLWithPath: filePath)
do {
try data.write(to: fileUrl)
PHPhotoLibrary.shared().performChanges({
// Since PHAssetChangeRequest doesn't support other video formats like .ogv, .webm or .3gp directly,
// you might be limited in the types of videos you can save to the Photos library.
if fileType == .jpg {
_ = PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: fileUrl)
} else if fileType == .mp4 || fileType == .threeGP {
_ = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: fileUrl)
} else {
completion(false, nil)
return
}
}) { success, error in
completion(success, error)
}
} catch {
print("Error saving data: \(error)")
completion(false, error)
} }