I am trying to download and store image from webURL to Document Directory.
Here is my code for that.
-(void)downloadPersonPhoto:(NSDictionary *)dict
{
NSString *imageUrlStr=[dict objectForKey:#"personPhoto"];
if ([[dict valueForKey:#"personAvatarStore"] isEqualToString:#"AMAZON"])
{
imageUrlStr = [imageUrlStr stringByReplacingOccurrencesOfString:#"original" withString:#"100x100"];
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [self getDocumentDirectory];
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"personPhoto"];
if (![fileManager fileExistsAtPath:dataPath]) {
[fileManager createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:nil]; //Create folder
}
NSString *fullPath = [dataPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.png",[dict valueForKey:#"personId"]]];
if (![fileManager fileExistsAtPath:fullPath]) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrlStr]];
[fileManager createFileAtPath:fullPath contents:imageData attributes:nil];
NSLog(#"Avatar Photo stored at: %#", fullPath);
}
}
Every time "Avatar photo stored at : ..." is going to print in console. But if I go to that path and check image then it has zero bytes of size and no any preview available.
webURL of image is correct I can also check from web Browser.
I don't know where is mistake in my code.
Can you please help me to solve this??
Thanks in advance.
dataWithContentsOfURL is not download image asynchronously from server. now if imagesize is big enough then it takes some time for downloading. and you are directly trying to store image to document directory. I think that create problem. you should use NSUrlSession for getting image and you should write data to local storage like documents directory from completion handler of NSUrlSession method call. you can use AFNetworking also to manage this kind od stuff.
Second thing use
[data writeToFile:path atomically:NO];
to store data to document directory and path is final path and should be unique for different data.
[NSData dataWithContentsOfURL] is not a asynchronously method
if you Debug you code you will find imagedata will be nil
you should first get imagedata then store it
you can use AFNetworing , SDWebImage framework or just use NSURLSession download it
here's one solution of my own
[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError * error) {
//error
if (error) {
//handle error
return;
}
if (data) {
//store imagedata
[data writeToFile:filepath atomically:NO];
}
}];
good day :)
Related
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.
I'm new to encryption and am trying to encrypt recordings with RNCryptor. The files are encrypted properly but, after decrypting, the created NSData causes the AVAudioPlayer to fail initialization. The method I'm using for the encryption and decryption are...
- (void)renameFileInDocumentsFolder:(NSString *)oldFilename withNewName:(NSString *)newFilename
{
NSFileManager *filemgr;
NSString *oldPath = [self getFilePathFromDocumentsFolder:oldFilename];
filemgr = [NSFileManager defaultManager];
NSData *data = [filemgr contentsAtPath:oldPath];
NSString *destPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[newFilename stringByAppendingString:#".m4a"]];
NSLog(#"DEST:%#", destPath);
NSError *error;
NSData *encryptedData = [RNEncryptor encryptData:data
withSettings:kRNCryptorAES256Settings
password:#"ABC123"
error:&error];
[encryptedData writeToFile:destPath atomically:YES];
[filemgr removeItemAtPath:oldPath error:&error];
}
-(NSData *)decryptFilePathFromDocumentsFolder:(NSString *)filename
{
AudioRecorderAppDelegate *appDelegate=[AudioRecorderAppDelegate sharedDelegate];
_cacheDirectory = [[[appDelegate applicationCacheDirectory]path]stringByAppendingPathComponent:#"Recordings"];
// NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *filePath = [_cacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.m4a", filename]];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSData *decData = [RNDecryptor decryptData:data withPassword:#"ABC123" error:&error];
return decData;
}
The AVAudioPlayer initialization is...
- (void)initializeAudioPlayer
{
NSData *recording = [self decryptFilePathFromDocumentsFolder:_fileNameTextField.text];
if(!audioPlayer)
{
NSError *error=nil;
audioPlayer = [[AVAudioPlayer alloc]
initWithData:recording fileTypeHint:#".m4a" error:&error];
if (error)
NSLog(#"Error: %#", [error localizedDescription]);
else
I don't know what I'm doing wrong or if RNCryptor is even meant to encrypt audio files but any help would be greatly appreciated.
There are lots of places where you're not checking errors, both in the cryptor calls, and in the reading and writing of the file. Make sure at each point that you actually have what you expect. Make sure the written and returned data are of reasonable sizes (about the same as the original file). Make sure the original file actually is playable. Makes sure after decryption you have precisely the same bytes as you started with.
RNCryptor doesn't care what it encrypts. But if you're doing this on the UI thread (which it looks like you're doing, reading a large file from disk can cause you're program to hang, in some cases long enough for the OS to kill you. Generally large file operations (like large audio files) need to be done asynchronously.
NSString *path = dict[#"fileURL"];
NSData *imageData = [NSData dataWithContentsOfFile:path];
imageData is null
dict[#"fileURL"] =
/var/mobile/Containers/Data/Application/0906AB53-CE1F-45EF-8E00-9D0B7C98956C/Documents/ccc/image/1446625127.png
I download the device container and the image did exist.
What's wrong?
It's failed to retrieve your file. Use -dataWithContentsOfFile:options:error: to find why it is failed.
NSError* error = nil;
NSString *path = dict[#"fileURL"];
NSData *data = [NSData dataWithContentsOfFile:path options: 0 error: &error];
if (data == nil)
{
NSLog(#"Failed to read file, error %#", error);
}
else
{
// parse the value
}
I thing you should need to use [NSFileManager defaultManager] to store and fetch the image in your application storage instead of writing static path.
Static path is ok withing the simulator but in the real environment you should need to have the actual path. So [NSFileManager defaultManager] will help you.
if([[NSFileManager defaultManager] fileExistsAtPath:filepath)
{
NSData *data = [[NSFileManager defaultManager] contentsAtPath:filepath];
}
else
{
NSLog(#"File not exits");
}
In filepath you need to pass only your folder and file name. not full path like yours.
IE.
filepath = "Documents/ccc/image/1446625127.png";
NOTE:
This is not a actual answer. This will help you to manage the image into your application storage.
I am trying get NSData of an own file.
My code is as follow, but NSData returned is always nil… (As you can see, I check if the file exists previously)
if ([[NSFileManager defaultManager] fileExistsAtPath:path]){
NSData * data = [[NSFileManager defaultManager] contentsAtPath:path];
}
Any idea? Thanks!
It's possible that path is a folder, in which case fileExistsAtPath will return YES, but no data can be read.
You can add some extra debugging by reading the data as follows:
NSError* error = nil;
NSData* data = [NSData dataWithContentsOfFile:path options:0 error:&error];
NSLog(#"Data read from %# with error: %#", path, error);
The log output will display the actual error that occurred.
Use This code it works
NSString *path = [pathURL filePath];
if([[NSFileManager defaultManager] fileExistsAtPath:path)
{
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
}
else
{
NSLog(#"File not exits");
}
I'm trying to load images using the following method.
I first check if I already have the images on the disk, if I do then I will just get the image data from the disk and load it otherwise I will get the image from the server and write to disk so that the second time that I need the image I won't have to access the server.
The problem is it doesn't seem to write or read from the disk. Everytime that I want to load images for the second time it's still reads them from the server and the NSLog(#"disk"); never gets called.
I don't know what I'm doing wrong if anyone has any idea?
-(UIImage *)imageWith:(NSString *)imageName isPreview:(BOOL)preview
{
//imageName is something like "56.jpg"
NSString *mainOrPreview = #"Preview";
if (!preview) {
mainOrPreview = #"Main";
}
NSString *pathSuffix = [[#"Images" stringByAppendingPathComponent:mainOrPreview] stringByAppendingPathComponent:imageName];
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:pathSuffix];
NSData *imageData = [NSData dataWithContentsOfFile:path];
if (imageData) {
NSLog(#"disk");
}
if (!imageData && [self connected]) {
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[serverURL stringByAppendingPathComponent: pathSuffix]]];
if (imageData) {
[imageData writeToFile:path atomically:YES];
}
NSLog(#"server");
}
return [UIImage imageWithData:imageData];
}
The problem is that the directories don't exist beyond Documents. Therefore the attempts to write the files are failing. It's always a good idea to use the file methods that have the NSError parameters so you can check the result.
You need to update only the code that actually writes the image from the server.
if (!imageData && [self connected]) {
// This needs to be done in on a background thread!
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[serverURL stringByAppendingPathComponent: pathSuffix]]];
if (imageData) {
[[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
[imageData writeToFile:path atomically:YES];
}
NSLog(#"server");
}