I am creating a file like so:
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];
and I am displaying it in an UIDocumentInteractionController like so:
if (_previewItemURL) {
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:_previewItemURL];
documentInteractionController.delegate = self;
[documentInteractionController presentPreviewAnimated:YES];
}
However, sometimes the PDF file I am saving off bytes are way too big, sometimes 5.5MB, which causes UIDocumentInteractionController to some time to load the PDF. I was doing some reading here https://stackoverflow.com/a/27863508/979331 and it is suggested to create a 'mapped' file. My question is I don't understand how to create one. I have been googling like crazy for the past two days and I just don't understand it.
I think the issue is with the PDF because I tried this:
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];
with a PDF that was 5.5MB and everything seemed fine, could the issue be with how I getting the PDF? I am getting the bytes from a web service, here is my call:
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];
});
});
}
}
}];
And here is 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 is a string and that is the web service URL
You're not "creating" a mapped file. You're reading it into NSData as mapped to the bytes in the file. That means, that in-memory NSData bytes are underneath mapped to bytes in the file.
Here is a way to read a file as mapped:
https://github.com/atomicbird/atomictools/blob/master/NSData%2BreallyMapped.h
If you can't pass NSData to the controller for preview, mapping makes no sense. Even if you can, you have to be sure that controller won't copy your data before it is used.
Consider using PDFKit framework, where you can initialize PDFDocument with NSData and display it in PDFView.
Your questions;
create a 'mapped' file. My question is I don't understand how to
create one.
So let me point out that UIDocumentInteractionController has no inputs that accept NSData, so you wouldn't be able to create a memory mapped file to use with it. I also examined the header for any other clues, and didn't find any.
https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller
In looking for a solution, I saw that QLPreviewController mentions 'data' but I find it didn't accept NSData either.
https://developer.apple.com/documentation/quicklook/qlpreviewcontroller
I finally settled on WKWebView in WebKit, which does support using NSData, that is Memory Mapped, and loads quite quickly with a 15 MB PDF full of pictures and text I made.
I created a project with the large PDF and tried it out, it works fine. Please feel free to examine it.
https://github.com/eSpecialized/PDFDocViewerMapped
How 'mapped' works;
Anything that can take NSData can use a mapped file unless it needs the entire set of data at once. A PDF with a single image vs a multipage PDF are good examples of what can't be mapped and what can be mapped.
NSError *errors;
NSData *docFileData = [NSData dataWithContentsOfFile:docFileWithPath options:NSDataReadingMappedAlways error:&errors];
Options for NSData mapped;
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.
could the issue be with how I getting the PDF?
Fetching a PDF remotely means you must download the document.
Think of what you are doing, fetching the Pdf, saving it locally, then opening it in the UIDocument Interaction controller which takes URL's and not NSData.
I hope this meets your criteria for a solution;
UIDocumentInteractionController limitations with requiring URL's
WKWebView - allows using NSData that can be memory mapped.
3rd party options for PDF viewing - anything that can accept NSData is a
candidate for use. Look for CocoaPods and on GitHub for PDF, iOS PDF,
etc.
Related
I am currently working on a iOS application in which I show a download link to download a Excel sheet. I want to save that downloaded file to a location of my iOS device where all my downloaded applications are stored by default, so that I can access it later form my device without opening the application.
Like in android devices, files are stored in my files from where we can access them later on.
Is there any approach to achieve this in iOS.
You can store directly on Document Directory as below
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"tempfile"]]; // It may change according to your need
NSError * error = nil;
[imageData writeToFile:path options:NSDataWritingAtomic error:&error];
Here ,imageData is my data to be write on Document directory
And by saving on Document Directory you can get it from there
NSString * str = "Your Url"
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:str]
cachePolicy:NSURLCacheStorageAllowed
timeoutInterval:20];
NSURLResponse *response;
NSError *error;
NSData * data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
[data writeToFile:filePath atomically:YES];
You can try with it for once
// NSData downloads any file from an URL
NSURL* url = [NSURL URLWithString:#"http://192.168.0.8/PoweredByMacOSX.gif"];
NSData* data = [NSData dataWithContentsOfURL:url];
// get the documents directory
NSArray* pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documentsDir = documentsDirectory = [pathArray objectAtIndex:0];
NSString* localFile =
[documentsDir stringByAppendingPathComponent:#"webfile.png"];
// write the downloaded file to documents dir
[data writeToFile:localFile atomically:YES];
I tried saving a file into NSLibraryDirectory with the below code:
if (!userDir) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
documentsDirectory = [[[documentsDirectory stringByAppendingPathComponent:#"Caches"] stringByAppendingPathComponent:#"account"] stringByAppendingPathComponent:#"test"];
NSString *imageDir = [[documentsDirectory stringByAppendingPathComponent:#"Root"] stringByAppendingPathComponent:#"imageType"];
NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:imageDir withIntermediateDirectories:YES attributes:nil error:&error];
NSString *fileName = #"avatar"
NSString filePath = [imageDir stringByAppendingPathComponent:fileName];
}
NSData *imagedata = **imagedata**
NSError *saveError = nil;
[imageData writeToFile:filePath options:NSDataWritingWithoutOverwriting error:&saveError];
Image is saved and I can load it using the following method:
NSError *error = nil;
NSData *imageData = [[NSData alloc] initWithContentsOfFile:filePath options:NSDataReadingUncached error:&error];
When I kill the app and launch it again, the file is missing. Am I doing anything wrong here?
You're saving it in the Caches directory, which the system may clear whenever your app is not running. If you want the file to stick around more permanently you should store it somewhere else, like the Documents directory.
I am new iPhone developer.I am developing iPhone app in this app Google drive integration it is successfully.but I want to download jpg,png,pdf,doc.ppt,rtf,etc file are download.
all file and folder display in Table view.I want do download the selected file from the table view.so how can do this ?
I am read this link https://developers.google.com/drive/v2/reference/files/get but i can not understand.here how to call all this function.
I want to download file's from Google Drive and save in document directory.
I am try this code.
NSString *downloadedString = file.downloadUrl; // file is GTLDriveFile
NSLog(#"%#",file.downloadUrl);
GTMHTTPFetcher *fetcher = [self.driveService.fetcherService fetcherWithURLString:downloadedString];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error)
{
if (error == nil)
{
if(data != nil)
{
GTLDriveFile *file = [driveFiles objectAtIndex:indexPath.row];
NSString *filename=file.title;
//this variable globalObj.documentPath is global variable for document directory path.
filename = [globalObj.documentPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",filename]];
filename=[filename stringByReplacingOccurrencesOfString:#"file:/private" withString:#"file:///"];
NSLog(#"File name : %#",filename);
NSURL *targetURL=[[NSURL alloc] initFileURLWithPath:downloadedString];
NSData* Data = [[NSData alloc]initWithContentsOfURL:targetURL];
[Data writeToFile:filename atomically:YES];
NSLog(#"my path:%#",filename);
}
}
else
{
NSLog(#"Error - %#", error.description);
}
}];
so places help me.I want to download file and save in document directory.
any link for code places suggestion me.
Thanks
you does not have to do all this stuff.
here is just simple code
image url is from dropbox.
NSString *stringURL = #"https://dl.dropboxusercontent.com/s/pquhnr5pt37uk6q/backkk.jpeg?dl=1&token_hash=AAEXBejAXoD__RPMBom6nL2F5_Uhu62ed0puhtLIt2FGug";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"yourfile.png"];
[urlData writeToFile:filePath atomically:YES];
}
and finally you can represent it any way you want.
If you want to download large data.then use Threading concept.
if you use imageview programetically then write below code..... myimg=[[UIImageView alloc]initWithFrame:CGRectMake(30, 100, 100, 100)];
myimg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://dl.dropboxusercontent.com/s/pquhnr5pt37uk6q/backkk.jpeg?dl=1&token_hash=AAEXBejAXoD__RPMBom6nL2F5_Uhu62ed0puhtLIt2FGug"]]];
[self.view
addSubview:myimg]; it's working
I want to save a lot (800-2000) images from server to iPhone app directory (Documents directory).
First of all I have to check if those image are already on the iPhone directory.
I use this code to download one image:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://a3.twimg.com/profile_images/414797877/05052008321_bigger.jpg"]];
[NSURLConnection connectionWithRequest:request delegate:self];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *localFilePath = [documentsDirectory stringByAppendingPathComponent:#"pkm.jpg"];
NSData *thedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://a3.twimg.com/profile_images/414797877/05052008321_bigger.jpg"]];
[thedata writeToFile:localFilePath atomically:YES];
Please help me, any suggestions:
1. Save all images with the original name on iPhone directory, NOT "pkm.jpg"
2. Save images with names: from 05052008321_bigger to 05052008350_bigger
3. Check if images are downloaded already, DON'T Download again.
I know, maybe this is more than a question. But some suggestions, directions will be really good.
Thanks in advance.
A couple of reactions:
You're initiating a NSURLConnection connectionWithRequest (which triggers the NSURLConnectionDataDelegate methods), but you then apparently disregard that and initiate a dataWithContentsOfURL. You should pick one or the other, but don't do both.
I'd suggest you pursue a NSOperation-based solution because you'll definitely want to (a) enjoy concurrency; but (b) limit the concurrent operations to some reasonable number (say 4 or 5), otherwise you'll have requests timing out and failing.
In terms of getting the filename, you can retrieve the filename using lastPathComponent from the NSURL. (My download operation, used below, actually will automatically use this if you don't provide an explicit filename.)
You haven't illustrated how you're determining the list of filenames from the remote server, so we'd have to see how you know what images there are to retrieve.
If this is for your own purposes, this is fine, but I've heard claims that Apple rejects apps that request too much data over a cellular connection (and 2000 images would certainly qualify). Frankly, even if Apple doesn't raise a fuss, you really should be asking the user before using that much of their data plan. You can use Reachability to determine whether the user is connecting via wifi or via cellular, and if the latter, you may want to present a warning.
But I'd suggest something that looks like (assuming you have some NSArray with NSString versions of the URL's ... obviously adjust this for whatever form your list of URLs is in):
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount = 4;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
for (NSString *urlString in urlStrings)
{
NSURL *url = [NSURL URLWithString:urlString];
NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
if (![fileManager fileExistsAtPath:path]) {
DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url];
downloadOperation.downloadCompletionBlock = ^(DownloadOperation *operation, BOOL success, NSError *error) {
if (error) NSLog(#"download of %# failed: %#", operation.url, error);
};
[downloadQueue addOperation:downloadOperation];
}
}
And that download operation might be something like this. Obviously, use whatever NSOperation based downloader you want, but I'd suggest you use one that doesn't load the whole download into memory, but rather one that streams it directly to persistent storage.
If you don't want to get that fancy, you could just do something like:
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount = 4;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
for (NSString *urlString in urlStrings)
{
NSURL *url = [NSURL URLWithString:urlString];
NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
if (![fileManager fileExistsAtPath:path]) {
[downloadQueue addOperationWithBlock:^{
NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
NSData *data = [NSData dataWithContentsOfURL:url];
if (data)
[data writeToFile:path atomically:YES];
}];
}
}
Clearly, use whatever download operation class you want for downloading, but hopefully you get the basic idea. Create a NSOperation-based download, and then submit one for every file that needs to get downloaded.
I'm not sure the best method to serialize the information (naively, you could just write the NSDictionary to the disk). I would have a large NSDictionary (which could be broken up into smaller ones, up to you how to do that). The NSDictionary would take the image name "05052008321_bigger" and map it to a simple #YES.
When the app is in a position to download a new image, it would read the NSDictionary from disk (can be read in a different thread), and check if the image name is in the dictionary. This allows lookups to be fast.
- (BOOL)checkIfImageHasBeenDownloaded:(NSString *)imageName
{
return [self.dictionaryNames objectForKey:imageName] != nil;
}
I'm trying to download content with an URL to a local file system in my iPad (cachedDir ? )
with this function:
- (IBAction)download:(id)sender {
NSURL *url = [NSURL URLWithString:#"http:www.google.de"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
[request setDownloadDestinationPath:#"/Users/ben/Desktop/my_file.pdf"]; // Which //directory??
}
Which directory do I have to choose for my path if I want to store the data as long as possible without getting rejected by Apple,
and how do I retrieve the data I've saved?
Can somebody help?
The best place to store documents that you want to keep around is your application's Documents directly. You can find the path to it like so:
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
Apple have a useful piece of documentation about the iOS file system and where to store particular types of files: http://developer.apple.com/library/mac/#documentation/FileManagement/Conceptual/FileSystemProgrammingGUide/FileSystemOverview/FileSystemOverview.html
Try out this my code for save pdf in document using below code
NSString *downloadUrl=[NSURL URLWithString:#"http:www.google.de"];
NSData *pdfData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:downloadUrl]];
//Store the Data locally as PDF File
NSString *resourceDocPath = [[NSString alloc] initWithString:[[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:#"Documents"]];
NSString *pdf1 = #"title.pdf";
NSString *filePath = [resourceDocPath stringByAppendingPathComponent:pdf1];
[pdfData writeToFile:filePath atomically:YES];
and retrive your file using
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSURL *pdfURL = [[NSBundle bundleWithPath:[paths objectAtIndex:0]] URLForResource:[NSString stringWithFormat:#"title.pdf"] withExtension:nil];
NSLog(#"pDF URl %#", pdfURL);
pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);