Iphone iOS下载并在应用程序内保存图像

Iphone iOS下载并在应用程序内保存图像,iphone,cocoa-touch,ios,ipad,Iphone,Cocoa Touch,Ios,Ipad,我是否可以从网站下载图像并将其永久保存在我的应用程序中?我真的不知道,但这将是我的应用程序的一个很好的功能。带缓存的异步下载图像 您无法在应用程序包中保存任何内容,但可以使用+[NSData dataWithContentsOfURL:将图像存储在应用程序的文档目录中,例如: NSData *imageData = [NSData dataWithContentsOfURL:myImageURL]; NSString *imagePath = [[NSSearchPathForDirector

我是否可以从网站下载图像并将其永久保存在我的应用程序中?我真的不知道,但这将是我的应用程序的一个很好的功能。

带缓存的异步下载图像


您无法在应用程序包中保存任何内容,但可以使用
+[NSData dataWithContentsOfURL:
将图像存储在应用程序的文档目录中,例如:

NSData *imageData = [NSData dataWithContentsOfURL:myImageURL];
NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"/myImage.png"];
[imageData writeToFile:imagePath atomically:YES];

不完全是永久的,但至少在用户删除应用程序之前,它会一直存在。

这是主要概念。玩得开心;)


由于我们现在使用IO5,您不再需要将图像写入磁盘。
现在可以在coredata二进制属性上设置“允许外部存储”。 根据苹果发布说明,其含义如下:

诸如图像缩略图之类的小数据值可以有效地存储在存储库中 数据库,但大型照片或其他媒体最好由 文件系统。现在可以指定托管 对象属性可以存储为外部记录-请参阅 启用后,如果 它应该将数据直接保存在数据库中,或者将URI存储到 它为您管理的单独文件。您不能基于 如果使用此选项,则为二进制数据属性的内容


虽然这里的其他答案确实有效,它们确实不是应该在生产代码中使用的解决方案。(至少不需要修改)

问题 这些答案的问题是,如果它们是按原样实现的,并且不是从后台线程调用的,那么它们将在下载和保存图像时阻止主线程。这是坏的

如果主线程被阻止,在图像下载/保存完成之前,不会进行UI更新。作为这意味着什么的一个例子,假设您向应用程序添加一个UIActivityIndicatorView,以向用户显示下载仍在进行中(我将在整个回答中以此为例),大致控制流程如下:

  • 已加载负责启动下载的对象
  • 告诉活动指示器开始设置动画
  • 使用
    +[NSData dataWithContentsOfURL:][/code>
  • 保存刚下载的数据(图像)
  • 告诉活动指示器停止设置动画
  • 现在,这似乎是合理的控制流,但它掩盖了一个关键问题

    当您在主(UI)线程上调用activity indicator的startAnimating方法时,此事件的UI更新直到下次更新时才会真正发生,这就是第一个主要问题所在

    在这个更新有机会发生之前,下载会被触发,因为这是一个同步操作,它会阻止主线程,直到它完成下载(保存也有同样的问题)。这实际上会阻止活动指示器启动其动画。之后,调用activity indicator的stopAnimating方法并期望一切都好,但事实并非如此

    在这一点上,你可能会发现自己想知道以下几点

    为什么我的活动指示器从未出现过

    好吧,这样想吧。您告诉指示器开始,但在下载开始之前它没有机会。下载完成后,告诉指示器停止设置动画。由于主线程在整个操作过程中被阻塞,因此您实际看到的行为更像是告诉指示器开始,然后立即告诉它停止,即使其间有一个(可能)大型下载任务

    现在,在最佳情况下,所有这些都会导致糟糕的用户体验(仍然非常糟糕)。即使你认为这没什么大不了的,因为你只下载了一个小图片,下载几乎是瞬间发生的,但情况并不总是这样。您的一些用户可能internet连接速度较慢,或者服务器端出现问题,无法立即/根本无法启动下载

    在这两种情况下,当你的下载任务在等待下载完成或服务器响应请求时,应用程序将无法处理UI更新,甚至无法触摸事件

    这意味着,从主线程同步下载可能会阻止您实现任何向用户指示当前正在进行下载的操作。由于触摸事件也在主线程上处理,因此也排除了添加任何类型的取消按钮的可能性

    然后,在最坏情况下,您将开始收到说明以下内容的崩溃报告

    异常类型:000000 20异常代码:0x8badf00d

    通过异常代码
    0x8badf00d
    可以很容易地识别这些异常,该异常代码可以理解为“吃了变质食品”。这个异常是由看门狗计时器引发的,它的任务是监视阻止主线程的长时间运行的任务,并在这种情况持续太久时杀死有问题的应用程序。可以说,这仍然是一个糟糕的用户体验问题,但如果这种情况开始出现,应用程序已经越过了糟糕的用户体验和糟糕的用户体验之间的界限

    这里有一些关于同步网络的更多信息,这些信息是什么导致了这种情况的发生(为了简洁起见,缩短了)

    网络应用程序中看门狗超时崩溃的最常见原因是主线程上的同步联网。这里有四个促成因素:

  • 同步网络-这是您发出网络请求并阻止等待响应的地方
  • 主线程同步网络通常不太理想,但如果在t上进行,则会导致特定的问题
    NSURL *url = [NSURL URLWithString:@"http://example.com/yourImage.png"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    path = [path stringByAppendingString:@"/yourLocalImage.png"];
    [data writeToFile:path atomically:YES];
    
    - (NSURL *)documentsDirectoryURL
    {
        NSError *error = nil;
        NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory
                                                            inDomain:NSUserDomainMask
                                                   appropriateForURL:nil
                                                              create:NO
                                                               error:&error];
        if (error) {
            // Figure out what went wrong and handle the error.
        }
        
        return url;
    }
    
    // Start the activity indicator before moving off the main thread
    [self.activityIndicator startAnimating];
    // Move off the main thread to start our blocking tasks.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Create the image URL from a known string.
        NSURL *imageURL = [NSURL URLWithString:@"http://www.google.com/images/srpr/logo3w.png"];
        
        NSError *downloadError = nil;
        // Create an NSData object from the contents of the given URL.
        NSData *imageData = [NSData dataWithContentsOfURL:imageURL
                                                  options:kNilOptions
                                                    error:&downloadError];
        // ALWAYS utilize the error parameter!
        if (downloadError) {
            // Something went wrong downloading the image. Figure out what went wrong and handle the error.
            // Don't forget to return to the main thread if you plan on doing UI updates here as well.
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.activityIndicator stopAnimating];
                NSLog(@"%@",[downloadError localizedDescription]);
            });
        } else {
            // Get the path of the application's documents directory.
            NSURL *documentsDirectoryURL = [self documentsDirectoryURL];
            // Append the desired file name to the documents directory path.
            NSURL *saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:@"GCD.png"];
    
            NSError *saveError = nil;
            BOOL writeWasSuccessful = [imageData writeToURL:saveLocation
                                                    options:kNilOptions
                                                      error:&saveError];
            // Successful or not we need to stop the activity indicator, so switch back the the main thread.
            dispatch_async(dispatch_get_main_queue(), ^{
                // Now that we're back on the main thread, you can make changes to the UI.
                // This is where you might display the saved image in some image view, or
                // stop the activity indicator.
                
                // Check if saving the file was successful, once again, utilizing the error parameter.
                if (writeWasSuccessful) {
                    // Get the saved image data from the file.
                    NSData *imageData = [NSData dataWithContentsOfURL:saveLocation];
                    // Set the imageView's image to the image we just saved.
                    self.imageView.image = [UIImage imageWithData:imageData];
                } else {
                    NSLog(@"%@",[saveError localizedDescription]);
                    // Something went wrong saving the file. Figure out what went wrong and handle the error.
                }
                
                [self.activityIndicator stopAnimating];
            });
        }
    });
    
    // Start the activity indicator before starting the download task.
    [self.activityIndicator startAnimating];
    
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    // Use a session with a custom configuration
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    // Create the image URL from some known string.
    NSURL *imageURL = [NSURL URLWithString:@"http://www.google.com/images/srpr/logo3w.png"];
    // Create the download task passing in the URL of the image.
    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:imageURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        // Get information about the response if neccessary.
        if (error) {
            NSLog(@"%@",[error localizedDescription]);
            // Something went wrong downloading the image. Figure out what went wrong and handle the error.
            // Don't forget to return to the main thread if you plan on doing UI updates here as well.
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.activityIndicator stopAnimating];
            });
        } else {
            NSError *openDataError = nil;
            NSData *downloadedData = [NSData dataWithContentsOfURL:location
                                                           options:kNilOptions
                                                             error:&openDataError];
            if (openDataError) {
                // Something went wrong opening the downloaded data. Figure out what went wrong and handle the error.
                // Don't forget to return to the main thread if you plan on doing UI updates here as well.
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"%@",[openDataError localizedDescription]);
                    [self.activityIndicator stopAnimating];
                });
            } else {
                // Get the path of the application's documents directory.
                NSURL *documentsDirectoryURL = [self documentsDirectoryURL];
                // Append the desired file name to the documents directory path.
                NSURL *saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:@"NSURLSession.png"];
                NSError *saveError = nil;
                
                BOOL writeWasSuccessful = [downloadedData writeToURL:saveLocation
                                                              options:kNilOptions
                                                                error:&saveError];
                // Successful or not we need to stop the activity indicator, so switch back the the main thread.
                dispatch_async(dispatch_get_main_queue(), ^{
                    // Now that we're back on the main thread, you can make changes to the UI.
                    // This is where you might display the saved image in some image view, or
                    // stop the activity indicator.
                    
                    // Check if saving the file was successful, once again, utilizing the error parameter.
                    if (writeWasSuccessful) {
                        // Get the saved image data from the file.
                        NSData *imageData = [NSData dataWithContentsOfURL:saveLocation];
                        // Set the imageView's image to the image we just saved.
                        self.imageView.image = [UIImage imageWithData:imageData];
                    } else {
                        NSLog(@"%@",[saveError localizedDescription]);
                        // Something went wrong saving the file. Figure out what went wrong and handle the error.
                    }
                    
                    [self.activityIndicator stopAnimating];
                });
            }
        }
    }];
    
    // Tell the download task to resume (start).
    [task resume];
    
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                          delegate:self
                                                     delegateQueue:[NSOperationQueue mainQueue]];
    
    // Use the default session configuration for the manager (background downloads must use the delegate APIs)
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    // Use AFNetworking's NSURLSessionManager to manage a NSURLSession.
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    
    // Create the image URL from some known string.
    NSURL *imageURL = [NSURL URLWithString:@"http://www.google.com/images/srpr/logo3w.png"];
    // Create a request object for the given URL.
    NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
    // Create a pointer for a NSProgress object to be used to determining download progress.
    NSProgress *progress = nil;
    
    // Create the callback block responsible for determining the location to save the downloaded file to.
    NSURL *(^destinationBlock)(NSURL *targetPath, NSURLResponse *response) = ^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        // Get the path of the application's documents directory.
        NSURL *documentsDirectoryURL = [self documentsDirectoryURL];
        NSURL *saveLocation = nil;
        
        // Check if the response contains a suggested file name
        if (response.suggestedFilename) {
            // Append the suggested file name to the documents directory path.
            saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:response.suggestedFilename];
        } else {
            // Append the desired file name to the documents directory path.
            saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:@"AFNetworking.png"];
        }
    
        return saveLocation;
    };
    
    // Create the completion block that will be called when the image is done downloading/saving.
    void (^completionBlock)(NSURLResponse *response, NSURL *filePath, NSError *error) = ^void (NSURLResponse *response, NSURL *filePath, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            // There is no longer any reason to observe progress, the download has finished or cancelled.
            [progress removeObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
            
            if (error) {
                NSLog(@"%@",error.localizedDescription);
                // Something went wrong downloading or saving the file. Figure out what went wrong and handle the error.
            } else {
                // Get the data for the image we just saved.
                NSData *imageData = [NSData dataWithContentsOfURL:filePath];
                // Get a UIImage object from the image data.
                self.imageView.image = [UIImage imageWithData:imageData];
            }
        });
    };
    
    // Create the download task for the image.
    NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request
                                                             progress:&progress
                                                          destination:destinationBlock
                                                    completionHandler:completionBlock];
    // Start the download task.
    [task resume];
    
    // Begin observing changes to the download task's progress to display to the user.
    [progress addObserver:self
               forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                           context:(void *)context
    {
        // We only care about updates to fractionCompleted
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(fractionCompleted))]) {
            NSProgress *progress = (NSProgress *)object;
            // localizedDescription gives a string appropriate for display to the user, i.e. "42% completed"
            self.progressLabel.text = progress.localizedDescription;
        } else {
            [super observeValueForKeyPath:keyPath
                                 ofObject:object
                                   change:change
                                  context:context];
        }
    }
    
    // Create the destination closure to pass to the download request. I haven't done anything with them
    // here but you can utilize the parameters to make adjustments to the file name if neccessary.
    let destination = { (url: NSURL!, response: NSHTTPURLResponse!) -> NSURL in
        var error: NSError?
        // Get the documents directory
        let documentsDirectory = NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory,
            inDomain: .UserDomainMask,
            appropriateForURL: nil,
            create: false,
            error: &error
        )
        
        if let error = error {
            // This could be bad. Make sure you have a backup plan for where to save the image.
            println("\(error.localizedDescription)")
        }
        
        // Return a destination of .../Documents/Alamofire.png
        return documentsDirectory!.URLByAppendingPathComponent("Alamofire.png")
    }
    
    Alamofire.download(.GET, "http://www.google.com/images/srpr/logo3w.png", destination)
        .validate(statusCode: 200..<299) // Require the HTTP status code to be in the Successful range.
        .validate(contentType: ["image/png"]) // Require the content type to be image/png.
        .progress { (bytesRead, totalBytesRead, totalBytesExpectedToRead) in
            // Create an NSProgress object to represent the progress of the download for the user.
            let progress = NSProgress(totalUnitCount: totalBytesExpectedToRead)
            progress.completedUnitCount = totalBytesRead
            
            dispatch_async(dispatch_get_main_queue()) {
                // Move back to the main thread and update some progress label to show the user the download is in progress.
                self.progressLabel.text = progress.localizedDescription
            }
        }
        .response { (request, response, _, error) in
            if error != nil {
                // Something went wrong. Handle the error.
            } else {
                // Open the newly saved image data.
                if let imageData = NSData(contentsOfURL: destination(nil, nil)) {
                    dispatch_async(dispatch_get_main_queue()) {
                        // Move back to the main thread and add the image to your image view.
                        self.imageView.image = UIImage(data: imageData)
                    }
                }
            }
        }
    
     [self setImageWithURL:user.user_ProfilePicturePath toControl:cell.imgView]; 
     
    -(void)setImageWithURL:(NSURL*)url toControl:(id)ctrl
    {
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
            if (image) {
                if([ctrl isKindOfClass:[UIButton class]])
                {
                    UIButton btn =(UIButton)ctrl;
                    [btn setBackgroundImage:image forState:UIControlStateNormal];
                }
                else
                {
                    UIImageView imgView = (UIImageView)ctrl;
                    imgView.image = image;
                }

    } } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"No Image"); }]; [operation start];}
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self performSelectorInBackground:@selector(loadImageIntoMemory) withObject:nil];
    
    }
    - (void)loadImageIntoMemory {
        NSString *temp_Image_String = [[NSString alloc] initWithFormat:@"http://yourwebsite.com/MyImageName.jpg"];
        NSURL *url_For_Ad_Image = [[NSURL alloc] initWithString:temp_Image_String];
        NSData *data_For_Ad_Image = [[NSData alloc] initWithContentsOfURL:url_For_Ad_Image];
        UIImage *temp_Ad_Image = [[UIImage alloc] initWithData:data_For_Ad_Image];
        [self saveImage:temp_Ad_Image];
        UIImageView *imageViewForAdImages = [[UIImageView alloc] init];
        imageViewForAdImages.frame = CGRectMake(0, 0, 320, 50);
        imageViewForAdImages.image = [self loadImage];
        [self.view addSubview:imageViewForAdImages];
    }
    - (void)saveImage: (UIImage*)image {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString* path = [documentsDirectory stringByAppendingPathComponent: @"MyImageName.jpg" ];
        NSData* data = UIImagePNGRepresentation(image);
        [data writeToFile:path atomically:YES];
    }
    - (UIImage*)loadImage {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString* path = [documentsDirectory stringByAppendingPathComponent:@"MyImageName.jpg" ];
        UIImage* image = [UIImage imageWithContentsOfFile:path];
        return image;
    }
    
    - (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
    {
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                                   if ( !error )
                                   {
                                       UIImage *image = [[UIImage alloc] initWithData:data];
                                       completionBlock(YES,image);
                                   } else{
                                       completionBlock(NO,nil);
                                   }
                               }];
    }
    
    NSURL *imageUrl = //...
    
    [[MyUtilManager sharedInstance] downloadImageWithURL:[NSURL URLWithString:imageURL] completionBlock:^(BOOL succeeded, UIImage *image) {
        //Here you can save the image permanently, update UI and do what you want...
    }];
    
        + (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
            {
                NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
                [NSURLConnection sendAsynchronousRequest:request
                                                   queue:[NSOperationQueue mainQueue]
                                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                                           if ( !error )
                                           {
                                               UIImage *image = [[UIImage alloc] initWithData:data];
                                               completionBlock(YES,image);
                                           } else{
                                               completionBlock(NO,nil);
                                           }
                                       }];
            }
    
    +(void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
     {
     NSURLSessionDataTask*  _sessionTask = [[NSURLSession sharedSession] dataTaskWithRequest:[NSURLRequest requestWithURL:url]
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error != nil)
            {
              if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection)
                    {
                        completionBlock(NO,nil);
                    }
             }
        else
         {
          [[NSOperationQueue mainQueue] addOperationWithBlock: ^{
                            dispatch_async(dispatch_get_main_queue(), ^{
                            UIImage *image = [[UIImage alloc] initWithData:data];
                            completionBlock(YES,image);
    
                            });
    
          }];
    
         }
    
                                                }];
        [_sessionTask resume];
    }
    
    func dowloadAndSaveFile(from url: URL) {
        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
            var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            documentsURL.appendPathComponent(url.lastPathComponent)
            return (documentsURL, [.removePreviousFile])
        }
        let request = SessionManager.default.download(url, method: .get, to: destination)
        request.validate().responseData { response in
            switch response.result {
            case .success:
                if let destinationURL = response.destinationURL {
                    print(destinationURL)
                }
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
    }