Html 如何在 UIWebView 中保存内容以便在下次启动时更快加载?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1343515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 00:43:41  来源:igfitidea点击:

How to save the content in UIWebView for faster loading on next launch?

htmliosiphoneurluiwebview

提问by erotsppa

I know that there are some caching classes introduced in the iphone sdk recently, and there is also a TTURLRequest from three20's library that allows you to cache a request to a URL. However, because I am loading the web page in UIWebView by calling UIWebView's loadRequest, those techniques are not really applicable.

我知道最近在 iphone sdk 中引入了一些缓存类,并且还有一个来自three20's library 的 TTURLRequest 允许您将请求缓存到一个 URL。但是,因为我是通过调用 UIWebView 的 loadRequest 在 UIWebView 中加载网页,所以这些技术并不真正适用。

Any ideas how I can save a web page so that on next app launch, I don't have to fetch from the web again for the full page? The page itself already have some ajax mechanism that updates parts of itself automatically.

任何想法如何保存网页,以便在下一次应用程序启动时,我不必再次从网络获取完整页面?页面本身已经有一些 ajax 机制,可以自动更新自身的某些部分。

回答by yonel

There are a bunch of articles about the way the cache of the UIWebView works and the global feeling is that even if some mechanisms seems to work OK under MacOS X, the same approaches may have curious behavior under iPhone.

有很多关于 UIWebView 缓存工作方式的文章,总体感觉是,即使某些机制在 MacOS X 下似乎可以正常工作,但相同的方法在 iPhone 下可能会出现奇怪的行为。



HOWEVER,I'm doing it by playing with the global cache that is accessed by any NSURLConnection, UIWebViewincluded. And in my case, it works ;).

但是,我是通过使用 any 访问的全局缓存来实现的NSURLConnectionUIWebView包括。就我而言,它有效;)。

What you need to understand is the global flow:

您需要了解的是全局流:

  • YOU -> loadRequeston a UIWebView
  • This goes into NSURLCacheto ask "is there something cached for this request?":
  • 你 ->loadRequest在一个UIWebView
  • 这将NSURLCache询问“是否为该请求缓存了某些内容?”:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request

From that, here's what I do to handle the cache on the disk, on my side, to speed up the load of a UIWebView:

从那以后,这是我在磁盘上处理缓存的方法,以加快 UIWebView 的加载速度:

  • Subclass the NSURLCacheand override the get control over the -(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)requestselector
  • Reimplement this selector in such a way that if nothing has been written on the FS for this request (no cache), then do the request on your side and store the content on FS. Otherwise, return what has been previously cached.
  • Create an instance of your subclass and set it to the system so that it is used by your application
  • 子类化NSURLCache并覆盖对-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request选择器的控制
  • 以这样的方式重新实现这个选择器,如果这个请求没有在 FS 上写任何东西(没有缓存),那么在你这边执行请求并将内容存储在 FS 上。否则,返回先前缓存的内容。
  • 创建子类的实例并将其设置为系统,以便您的应用程序使用它


Now the code :

现在的代码:

MyCache.h

缓存文件

@interface MyCache : NSURLCache {
}
@end

MyCache.m

我的缓存文件

@implementation MyCache

-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSLog(@"CACHE REQUEST S%@", request);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:@"/"];
    if (tokens==nil) {
        NSLog(@"ignoring cache for %@", request);
        return nil;
    }
    NSString* pathWithoutRessourceName=@"";
    for (int i=0; i<[tokens count]-1; i++) {
        pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:@"%@%@", [tokens objectAtIndex:i], @"/"]];
    }
    NSString* absolutePath = [NSString stringWithFormat:@"%@%@", documentsDirectory, pathWithoutRessourceName];
    NSString* absolutePathWithRessourceName = [NSString stringWithFormat:@"%@%@", documentsDirectory, request.URL.relativePath];
    NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:@""];
    NSCachedURLResponse* cacheResponse  = nil;
    //we're only caching .png, .js, .cgz, .jgz
    if (
        [ressourceName rangeOfString:@".png"].location!=NSNotFound || 
        [ressourceName rangeOfString:@".js"].location!=NSNotFound ||
        [ressourceName rangeOfString:@".cgz"].location!=NSNotFound || 
        [ressourceName rangeOfString:@".jgz"].location!=NSNotFound) {
        NSString* storagePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, request.URL.relativePath];
        //this ressource is candidate for cache.
        NSData* content;
        NSError* error = nil;
        //is it already cached ? 
        if ([[NSFileManager defaultManager] fileExistsAtPath:storagePath]) {
            //NSLog(@"CACHE FOUND for %@", request.URL.relativePath);
            content = [[NSData dataWithContentsOfFile:storagePath] retain];
            NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"" expectedContentLength:[content length] textEncodingName:nil];
            cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
        } else {
            //trick here : if no cache, populate it asynchronously and return nil
            [NSThread detachNewThreadSelector:@selector(populateCacheFor:) toTarget:self withObject:request];
        }
    } else {
        NSLog(@"ignoring cache for %@", request);
    }
    return cacheResponse;
}

-(void)populateCacheFor:(NSURLRequest*)request {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    //NSLog(@"PATH S%@", paths);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:@"/"];
    NSString* pathWithoutRessourceName=@"";
    for (int i=0; i<[tokens count]-1; i++) {
        pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString     stringWithFormat:@"%@%@", [tokens objectAtIndex:i], @"/"]];
    }
    NSString* absolutePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, pathWithoutRessourceName];
    //NSString* absolutePathWithRessourceName = [NSString stringWithFormat:@"%@%@", documentsDirectory, request.URL.relativePath];
    //NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:@""];
    NSString* storagePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, request.URL.relativePath];
    NSData* content;
    NSError* error = nil;
    NSCachedURLResponse* cacheResponse  = nil;
    NSLog(@"NO CACHE FOUND for %@", request.URL);
    //NSLog(@"retrieving content (timeout=%f) for %@ ...", [request timeoutInterval], request.URL);
    content = [NSData dataWithContentsOfURL:request.URL options:1 error:&error];
    //NSLog(@"content retrieved for %@  / error:%@", request.URL, error);
    if (error!=nil) {
        NSLog(@"ERROR %@ info:%@", error, error.userInfo);
        NSLog(@"Cache not populated for %@", request.URL);
    } else {
        NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"" expectedContentLength:[content length] textEncodingName:nil];
        cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
        //the store is invoked automatically.
        [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
        BOOL ok;// = [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
        ok = [content writeToFile:storagePath atomically:YES];
        NSLog(@"Caching %@ : %@", storagePath , ok?@"OK":@"KO");
    }
    [pool release];
}
@end

And the use of it in your application:

并在您的应用程序中使用它:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* diskCachePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory, @"myCache"];
NSError* error; 
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:&error];
MyCache* cacheMngr = [[MyCache alloc] initWithMemoryCapacity:10000 diskCapacity:100000000 diskPath:diskCachePath];
[NSURLCache setSharedURLCache:cacheMngr];

This code deserves a lot of cleanup.. but the main things should be in there. I had a lot of trouble to get this working, hope this helps.

这段代码值得大量清理......但主要的东西应该在那里。我在完成这项工作时遇到了很多麻烦,希望这会有所帮助。

回答by yonel

I recently found this project under github : http://github.com/rs/SDURLCacheThe approach is quite the same as my previous answer described here How to save the content in UIWebView for faster loading on next launch?, but the code looks more polished so maybe it makes sense to give it a try.

我最近在 github 下发现了这个项目:http: //github.com/rs/SDURLCache该方法与我之前在此处描述的答案完全相同如何将内容保存在 UIWebView 中以便在下次启动时更快地加载?,但代码看起来更精致,所以也许尝试一下是有意义的。

回答by Alex Ford

If the page has AJAX already, why not store the JavaScript/HTML in the application bundle to start rather than downloading it on the first launch? Then load the page with the code Corey gave below and let the AJAX handle hitting the network for the updated parts of the page.

如果页面已经有 AJAX,为什么不将 JavaScript/HTML 存储在应用程序包中以启动而不是在第一次启动时下载它?然后使用 Corey 在下面给出的代码加载页面,让 AJAX 处理访问网络以获取页面的更新部分。

回答by Corey Floyd

You can save an HTML in the documents directory and load the page directly from the documents directory on launch.

您可以在文档目录中保存 HTML 并在启动时直接从文档目录加载页面。

To save the webview content: Reading HTML content from a UIWebView

保存 webview 内容: 从 UIWebView 读取 HTML 内容

To load:

装载:

    NSString* path = [[NSBundle mainBundle] pathForResource:@"about" ofType:@"html"];
    NSURL* url = [NSURL fileURLWithPath:path];

    NSURLRequest* request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];