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.
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 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 :)
I am implementing extensions in ios8. In this import function returns a url. I need to read data from it and save in my app.
Here is the code.
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSError *error;
__block NSData *data;
[fileCoordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
data = [NSData dataWithContentsOfURL:newURL];
//saving in document directory
}];
My question is, if file is too big, dataWithContentsOfURL results in crash due to out of memory.
I wanted a method to read data from that url in parts, save in my documents, then read next part and keep appending. Thus it won't have memory issue.
Can someone help.
Found two solutions.
1- Used NSURLConnection. Using NSFileHandle wrote data in file.
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1{
// [data appendData:data1];
[self.currentFileHandle seekToEndOfFile];
[self.currentFileHandle writeData:data1];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[self.currentFileHandle closeFile];
self.currentFileHandle = nil;
}
Instead of converting it in data and then saving. Used following to copy file.
NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingWithoutChanges error:nil byAccessor:^(NSURL *newURL) {
NSFileManager * fileManager = [[NSFileManager alloc] init];
NSError * error;
NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *urlDestination = [paths lastObject];
urlDestination = [urlDestination URLByAppendingPathComponent:[url lastPathComponent]];
if([[NSFileManager defaultManager] fileExistsAtPath:[urlDestination relativePath]]){
[[NSFileManager defaultManager] removeItemAtPath:[urlDestination relativePath] error:nil];
}
BOOL success = [fileManager copyItemAtURL:url toURL:urlDestination error:&error];
if (success) {
}
}];
I used 2nd solution. Sorry for wrong alignment. I tried a lot, but not
coming properly.
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 have a problem when downloading a file. I have a zip file that has 90 MB and it is filling my RAM memory to hudge amounts until the file is downloaded. Could somene help me with some example code that shows any better way to do this and not overload my RAM?
Thanks.
Code:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:#"http://autoskola.1e29g6m.xip.io/mobileData/slike3.zip"];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
if(!error){
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *zipPath = [path stringByAppendingPathComponent:#"slike3.zip"];
[data writeToFile:zipPath options:0 error:&error];
if(!error){
//unziping the files
ZipArchive *za = [[ZipArchive alloc] init];
if([za UnzipOpenFile:zipPath]){
BOOL ret = [za UnzipFileTo:path overWrite:YES];
if(NO == ret){} [za UnzipCloseFile];
}
}else{
NSLog(#"Error saving file %#", error);
}
}else{
NSLog(#"Error downloading zip file: %#", error);
}
});
You may want to consider looking into AFNetworking. It's probably the most commonly used library for accessing the internet. You can use it to download a file and take the output stream and write the data into a file. This will allow you to download and write the file as chunks of data instead of all at once. You'll want to take a look at the AFNetworking Docs for information on downloading the file. You'll also want to look into the docs for NSFileHandle for information on how to write data into a file.