Objective-C 为 PDF 创建映射文件



我正在创建一个这样的文件:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,PDFFile];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createFileAtPath:filePath contents:dataBytes attributes:nil];
}
_previewItemURL = [NSURL fileURLWithPath:filePath];

我在UIDocumentInteractionController中显示它,如下所示:

if (_previewItemURL) {
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:_previewItemURL];
documentInteractionController.delegate = self;
[documentInteractionController presentPreviewAnimated:YES];
}

但是,有时我保存的PDF文件字节太大,有时为5.5MB,这会导致UIDocumentInteractionController需要一段时间才能加载PDF。我 https://stackoverflow.com/a/27863508/979331 在这里做了一些阅读,建议创建一个"映射"文件。我的问题是我不明白如何创建一个。这两天我一直在疯狂地谷歌搜索,我只是不明白。

我认为问题出在 PDF 上,因为我尝试过这个:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pgnPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.pdf", @"example"]];
//filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,PDFFile];
//if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSString *newFile = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"pdf"];
[[NSFileManager defaultManager] copyItemAtPath:newFile toPath:pgnPath error:&error];
//[[NSFileManager defaultManager] createFileAtPath:filePath contents:dataBytes attributes:nil];
//}
//_previewItemURL = [[NSBundle mainBundle] URLForResource:@"example" withExtension:@"pdf"];
_previewItemURL = [NSURL fileURLWithPath:pgnPath];

使用5.5MB的PDF并且一切似乎都很好,问题可能出在我如何获取PDF上吗?我正在从 Web 服务获取字节,这是我的电话:

task = [dataSource.areaData GetPDFFileTestTwo:[NSString stringWithFormat:@"%@",encodedUrlStr] completion:^(NSData *data, NSURLResponse *response, NSError *error) {
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
NSData *dataBytes;
for (NSDictionary *dict in tableArray) {
NSString *base64 = dict[@"data"];
dataBytes = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
}
if (dataBytes) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory,PDFFile];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createFileAtPath:filePath contents:dataBytes attributes:nil];
}
_previewItemURL = [NSURL fileURLWithPath:filePath];
if (_previewItemURL) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:_previewItemURL];
documentInteractionController.delegate = self;
dispatch_async(dispatch_get_main_queue(), ^{
[documentInteractionController presentPreviewAnimated:YES];
});

});
}

}
}];

这是GetPDFFileTestTwo

-(NSURLSessionDataTask *)GetPDFFileTestTwo:(NSString *)PDFFile completion:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler{
NSString *FileBrowserRequestString = [NSString stringWithFormat:@"%@?PDFFile=%@",kIP,PDFFile];
NSURL *JSONURL = [NSURL URLWithString:FileBrowserRequestString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
if(completionHandler)
{
completionHandler(data, response, error);
}
}];
[dataTask resume];
return dataTask;
}

kIP 是一个字符串,即 Web 服务 URL

您不是在"创建"映射文件。您正在将其读入映射到文件中字节的NSData。这意味着,内存中的NSData字节位于映射到文件中的字节的下方。

以下是读取映射文件的方法:

https://github.com/atomicbird/atomictools/blob/master/NSData%2BreallyMapped.h

如果无法将NSData传递给控制器进行预览,则映射毫无意义。即使可以,您也必须确保控制器在使用数据之前不会复制您的数据。

考虑使用PDFKit框架,您可以在其中使用NSData初始化PDFDocument并以PDFView显示它。

您的问题;

  1. 创建一个"映射"文件。我的问题是我不明白如何 创建一个。

因此,让我指出UIDocumentInteractionController没有接受NSData的输入,因此您将无法创建与之一起使用的内存映射文件。我还检查了标题中的任何其他线索,但没有找到任何线索。 https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller

在寻找解决方案时,我看到QLPreviewController提到了"数据",但我发现它也不接受NSData。 https://developer.apple.com/documentation/quicklook/qlpreviewcontroller

我最终选择了WebKit中的WKWebView,它确实支持使用NSData,即Memory Mapped,并且加载速度非常快,其中包含我制作的图片和文本的15 MB PDF。

我用大PDF创建了一个项目并尝试了一下,它工作正常。请随时检查它。 https://github.com/eSpecialized/PDFDocViewerMapped

"映射"的工作原理;

任何可以采用NSData的东西都可以使用映射文件,除非它一次需要整个数据集。 具有单个图像的 PDF 与多页 PDF 是无法映射和可以映射的内容的很好示例。

NSError *errors;
NSData *docFileData = [NSData dataWithContentsOfFile:docFileWithPath options:NSDataReadingMappedAlways error:&errors];

映射了 NSData 的选项;

https://developer.apple.com/documentation/foundation/nsdatareadingoptions?language=objc

NSDataReadingMappedIfSafe // Hint to map the file in if possible and safe
NSDataReadingMappedAlways // Hint to map the file in if possible. This takes precedence over NSDataReadingMappedIfSafe if both are given.
  1. 问题可能出在我如何获取 PDF 上?

远程获取 PDF 意味着您必须下载文档。 想想你正在做什么,获取 Pdf,将其保存在本地,然后在 UIDocument 交互控制器中打开它,该控制器采用 URL 而不是 NSData。

我希望这符合您的解决方案标准;

  • UIDocumentInteractionController 限制与需要 URL 的

  • WKWebView - 允许使用可以进行内存映射的NSData。

  • 用于 PDF 查看的第三方选项 - 任何可以接受 NSData 的东西都是 候选使用。查找CocoaPods,并在GitHub上查找PDF,iOS PDF, 等。

最新更新