简体   繁体   English

WKWebView 无法使用 loadHTMLString(_, baseURL:) 加载图像和 CSS

[英]WKWebView fails to load images and CSS using loadHTMLString(_, baseURL:)

Apple's recommendation : 苹果的建议

In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView.在 iOS 8 及更高版本中运行的应用程序中,使用 WKWebView class 而不是使用 UIWebView。

Thus, I have replaced my good old UIWebView with a shiny new WKWebView .因此,我用 shiny 新的UIWebView替换了旧的WKWebView But what I thought to be an easy exercise (simply swapping the classes and replacing the delegate methods) turned out to be a real mess.但我认为是一个简单的练习(简单地交换类和替换委托方法)结果却是一团糟。

The Problem问题

When I load an HTML string using当我使用加载 HTML 字符串时

loadHTMLString(String, baseURL: URL?)

the web view loads and renders the pure HTML but it doesn't load any images or CSS files referenced inside the htmlString . web 视图加载并呈现纯 HTML 但它不加载 htmlString 中引用的任何图像或htmlString文件

This happens only on a real device !这只发生在真实设备上
In Simultor all referenced resources are loaded correctly.在 Simultor 中,所有引用的资源都被正确加载。

模拟器 真机

Example例子

I have defined a simple htmlString in my view controller class:我在我的视图 controller htmlString中定义了一个简单的 htmlString:

let imageName = "image.png"

let libraryURL: URL // The default Library URL

var htmlString: String {
    return "<html> ... <img src=\"\(imageName)\" /> ... </html>"
    // "..." represents more valid HTML code incl. header and body tags
}

The image is stored in the root Library folder so its URL is:该图像存储在根库文件夹中,因此其 URL 为:

let imageURL = libraryURL.appendingPathComponent(imageName)

Now I load the htmlString into the web view:现在我将htmlString加载到 web 视图中:

webView.loadHTMLString(htmlString, baseURL: libraryURL)

and it doesn't load the image even though the baseURL is set correctly.即使baseURL设置正确,它也不会加载图像。

Ideas for a Solution解决方案的想法

  1. Maybe WKWebView has a problem with resolving relative paths so my first idea was to use absolute paths inside the HTML string instead.也许WKWebView在解析相对路径时有问题,所以我的第一个想法是在 HTML 字符串中使用绝对路径
    → ❌ Doesn't work. → ❌ 不起作用。

  2. Two answers to another SO post suggested that using 另一个 SO 帖子的两个答案建议使用
    loadFileURL(URL, allowingReadAccessTo: URL)
    instead of loadHTMLString(...) works in iOS 9+.而不是loadHTMLString(...)适用于 iOS 9+。
    → ✅ That works. → ✅ 这行得通。

However, I cannot use solution 2 because my HTML files are encrypted and the decrypted files must not be stored on the disk.但是,我不能使用解决方案2,因为我的HTML文件是加密的,解密后的文件不能存储在磁盘上。

Question问题

Is there any way to load local resources like images and styles using the WKWebView 's有什么方法可以使用WKWebView加载本地资源,如图像和 styles

loadHTMLString(String, baseURL: URL?)

function? function? Or is still a bug in iOS 9+?或者仍然是 iOS 9+ 中的错误?

(I just cannot believe that Apple provides and recommends using a web view that cannot load any local web content from inside an HTML string?!) (我简直无法相信 Apple 提供并推荐使用 web 视图,它无法从 HTML 字符串中加载任何本地 web 内容?!)

Without taking a look at your actual project it's difficult to give some hundreed percent sure advices.如果不查看您的实际项目,就很难给出百分之百肯定的建议。

However:然而:

class ViewController: UIViewController {

    var webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "webView" : webView
        ]
        view.addSubview(webView)
        var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[webView]|", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views)
        constraints.appendContentsOf(NSLayoutConstraint.constraintsWithVisualFormat("V:|[webView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activateConstraints(constraints)

        let path = NSBundle.mainBundle().pathForResource("ios - WKWebView fails to load images and CSS using loadHTMLString(_, baseURL_) - Stack Overflow", ofType: "htm")
        let url = NSURL(fileURLWithPath: path!)
        webView.loadHTMLString(try! String(contentsOfURL: url), baseURL: url.URLByDeletingLastPathComponent)

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

I think the key point here is baseUrl parameter, you should setup it correctly.我认为这里的关键点是baseUrl参数,您应该正确设置它。 In my case i've used html's url without last path component - eg containing folder.在我的情况下,我使用了没有最后一个路径组件的 html url - 例如包含文件夹。 This works fine on both device & simulator - check device snapshot.这在设备和模拟器上都可以正常工作 - 检查设备快照。 I've uploaded sample project to https://github.com/soxjke/WKWebViewTest so you can take a look (i've removed codesigning info from git)我已将示例项目上传到https://github.com/soxjke/WKWebViewTest,因此您可以查看(我已从 git 中删除了代码设计信息)

设备快照

So, to recap - method is working, functionality is working, just you do something wrong with it.所以,总结一下 - 方法有效,功能有效,只是你做错了什么。 To help you get what's wrong with your solutions, i'll add some suggestions: 1. Remember, that simulator filesystem is case-insensitive , device filesystem is case-sensitive .为了帮助您找出解决方案的问题,我将添加一些建议: 1. 请记住,模拟器文件系统不区分大小写,设备文件系统区分大小写 So if you have your filenames in html in lowercase - this won't work on device.因此,如果您的文件名是小写的 html - 这将无法在设备上运行。 8fFsD.png != 8ffsd.png 2. Remember, that when copying resources, XCode ignores your folder structure. 8fFsD.png != 8ffsd.png 2. 请记住,在复制资源时,XCode 会忽略您的文件夹结构。 So if your html has <img src="./img/1.png"> and your XCOde project has folder structure like因此,如果您的 html 具有<img src="./img/1.png">并且您的 XCOde 项目具有类似的文件夹结构

test.htm

img/

    1.png 
    2.png

After build it will be flattened, so test.htm and 1.png and 2.png will reside on same level构建后它将被展平,因此 test.htm 和 1.png 和 2.png 将驻留在同一级别

test.htm
1.png 
2.png

I'm almost sure, after you verify these two assumptions, you'll get this method working.我几乎可以肯定,在您验证了这两个假设之后,您将可以使用此方法。

I had this problem today, I've found the solution and potentially the cause:我今天遇到了这个问题,我找到了解决方案和可能的原因:

loadHTMLString(String, baseURL: URL?) 

This function doesn't allow the rendered HTML to access local media, as far as I'm aware, this is because it would be an injection risk, this could allow rendered HTML to access and manipulate your local file system.这个函数不允许渲染的 HTML 访问本地媒体,据我所知,这是因为它会存在注入风险,这可能允许渲染的 HTML 访问和操作您的本地文件系统。 With a html string, that could come from anywhere or anyone.使用 html 字符串,它可以来自任何地方或任何人。

loadFileURL(URL, allowingReadAccessTo: URL)

With this function, you point the WKWebview to the html file in your FileManager, and to the containing folder with 'allowingReadAccessTo'.使用此函数,您将 WKWebview 指向 FileManager 中的 html 文件,并指向包含“allowingReadAccessTo”的文件夹。 Because the html is stored within the FileManager, it will allow the rendered HTML to access locally stored media.因为 html 存储在 FileManager 中,它将允许呈现的 HTML 访问本地存储的媒体。

If you don't have the html file stored locally for some reason(I assume you do), You could write the html sting into a .html file, then point to the URL of that file.如果由于某种原因您没有在本地存储 html 文件(我假设您有),您可以将 html 字符串写入 .html 文件,然后指向该文件的 URL。 However, this is just subverting Apple's protection, so do it at your own peril (don't do it).但是,这只是在破坏 Apple 的保护,因此请自担风险(不要这样做)。

This is just the solution that worked for me and my understanding of why we're having the problem to begin with.这只是对我有用的解决方案,也是我对为什么我们开始遇到问题的理解。

Edit #1: Typo.编辑 #1:错别字。

Edit #2: I've since found another nuance, When stating the 'allowingReadAccessTo:' URL, if the HTML itself needs to access things in parent folders (ie: .css, .js files), you need to specify the parent folder, not necessarily the location of the HTML itself, this will then implicitly allow access to the child folders as required also.编辑 #2:我发现了另一个细微差别,当声明“allowingReadAccessTo:”URL 时,如果 HTML 本身需要访问父文件夹(即:.css、.js 文件)中的内容,则需要指定父文件夹,不一定是 HTML 本身的位置,这也将隐式允许访问子文件夹。 For me, this problem was only apparent on a physical device, this didn't seem to have an effect whilst running in simulator, likely another discrepancy between how permissions work on simulator and a physical device.对我来说,这个问题只在物理设备上很明显,这在模拟器中运行时似乎没有影响,这可能是模拟器和物理设备上的权限工作方式之间的另一个差异。

Well you should be able to use local images and CSS files (and JavaScript files for that matter) with WKWebViews with the function that you have already found.好吧,您应该能够将本地图像和 CSS 文件(以及与此相关的 JavaScript 文件)与 WKWebViews 与您已经找到的功能一起使用。 My guess is that the problem is with your baseURL variable.我的猜测是问题出在您的baseURL变量上。

Update 7.5.2017: 2017 年 5 月 7 日更新:

I have completely updated the code from another SO answer of mine that used to be linked to my answer here.我已经完全更新了我的另一个 SO 答案中的代码, 该答案曾经链接到我的答案here。 I have a working project for loadHTMLString() and .loadFileURL()我有一个用于loadHTMLString().loadFileURL()的工作项目

Personally, I had to switch to using XWebView as the out-of-the-box behavior of WKWebView does not allow loading of local files.就个人而言,我不得不改用XWebView,因为WKWebView的开箱即用行为不允许加载本地文件。 XWebView tricks it by loading up a local web server in the background and directing local traffic thru it. XWebView 通过在后台加载本地 Web 服务器并通过它引导本地流量来欺骗它。 (XWebView is based on top of WKWebView) (XWebView 基于 WKWebView 之上)

Seems a bit overkill, but that is what I ended up having to do.似乎有点矫枉过正,但这就是我最终不得不做的。

I've been experimenting with this as well, with similar restrictions, and the problem appears to be that paths aren't resolved unless baseURL references the application bundle.我也一直在试验这个,有类似的限制,问题似乎是除非baseURL引用应用程序包,否则路径不会解析。 It doesn't work if you, for example, have something in the application's documents.例如,如果您在应用程序的文档中有某些内容,则它不起作用。

Edit: I have filed a radar for this rdar://29130863编辑:我已经为此rdar://29130863提交了一个雷达

Try to create baseURL using:尝试使用以下方法创建baseURL

let baseURL = URL(fileURLWithPath: "#path#")

instead of:而不是:

let baseURL = URL(string: "#path#")

The main difference is that the first method adds file:// prefix before the path.主要区别在于第一种方法在路径前添加了file://前缀。

You can base64 encode the images... I know that works.您可以对图像进行 base64 编码...我知道这可行。 Not sure if it will be appropriate for your use case though.不确定它是否适合您的用例。

Kind of funny, I just ran into this problem while doing the opposite - moving from base64 encoded to image files.有点有趣,我只是在做相反的事情时遇到了这个问题 - 从 base64 编码到图像文件。

When I used UIWebview, I used baseURL as,当我使用 UIWebview 时,我使用 baseURL 作为,

let baseUrl = NSURL(string: Bundle.main.path(forResource: "cms", ofType: "html")!)! as URL

webView.loadHTMLString(bodyPage, baseURL: baseUrl)

But for the WKWebView, I used baseURL as但是对于 WKWebView,我使用 baseURL 作为

let baseUrl = Bundle.main.bundleURL

webView.loadHTMLString(bodyPage, baseURL: baseUrl)

This works for me.这对我有用。

I know this is quite old already, but I ran into the exact same problem and it took me hours of trials and even to find this thread with the same problem (Xamarin Forms App)我知道这已经很老了,但我遇到了完全相同的问题,我花了数小时的时间进行试验,甚至找到了具有相同问题的线程(Xamarin Forms App)

My issue was: parsing remote HTML content into a string and also adding locally saved images (also downloaded dynamically, no resource of the app).我的问题是:将远程 HTML 内容解析为字符串并添加本地保存的图像(也是动态下载的,没有应用程序资源)。 On the simulator all works well, but on acutal device the local images are not showing (also no ? or anything indicating an error, just a blank frame).在模拟器上一切正常,但在实际设备上没有显示本地图像(也没有?或任何指示错误的内容,只是一个空白帧)。 The Xamarin webview also offers the "BaseURL" option which didn't help, also not to use the BaseURL on the custom iOS wkWebView. Xamarin webview 还提供了没有帮助的“BaseURL”选项,也不在自定义 iOS wkWebView 上使用 BaseURL。

The only working solution as pointed out by Scott above, is to write the HTML into a file and then use the "LoadFileUrl" function and allow read access to the base directory.正如上面 Scott 所指出的,唯一可行的解​​决方案是将 HTML 写入文件,然后使用“LoadFileUrl”函数并允许对基目录进行读取访问。 This also works with absolute file paths for images in the HTML (not only relative to the basedir, but of course somewhere within the basedir).这也适用于 HTML 中图像的绝对文件路径(不仅相对于 basedir,当然也适用于 basedir 内的某处)。

My custom webview renderer to load web and local content looks like this now:我的自定义 webview 渲染器加载 web 和本地内容现在看起来像这样:

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) {
    base.OnElementPropertyChanged(sender, e);
    NSUrl baseURL = new NSUrl(App.dirNews, true);
    string viewFile = Path.Combine(App.dirNews, "view.html");
    NSUrl fileURL = new NSUrl(viewFile, false);

    switch (e.PropertyName) {
        case "Url":
            System.Console.WriteLine("--- Loading Web page ---");
            System.Console.WriteLine("--- " + Element.Url + " ---");
            NSUrlRequest myRequest = new NSUrlRequest(new NSUrl(Element.Url), NSUrlRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, 120);
            Control.LoadRequest(myRequest);
            break;

        case "HTML":
            System.Console.WriteLine("--- Showing HTTP content ---");
            File.WriteAllText(viewFile, Element.HTML, System.Text.Encoding.UTF8);
            Control.LoadFileUrl(fileURL, baseURL);
            break;
    }
}

I was able to reproduce a similar issue.我能够重现类似的问题。 WKWebView loads my images specially if they are located remotely, apart from my app server. WKWebView 特别加载我的图像,如果它们位于远程,除了我的应用程序服务器。

For servers that are not SSL-secured (http instead of https), you can set your info.plist as per below:对于未受 SSL 保护(http 而不是 https)的服务器,您可以按如下设置info.plist

App Transport Security Settings

 - Allow Arbitrary Loads in Web Content (Set to YES)
 - Allow Arbitrary Loads (Set to YES)

The problem was actually in the server.问题实际上出在服务器上。 The server application was either:服务器应用程序是:

  • Changing the image src from "http://IP-or-domain/uploads/file.jpg" to "../../uploads/file.jpg"将图像源从"http://IP-or-domain/uploads/file.jpg"更改为"../../uploads/file.jpg"

- OR - - 或 -

  • The image src was "http://localhost/uploads/file.jpg" or "http://127.0.0.1/uploads/file.jpg" instead of "http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"图像 src 是"http://localhost/uploads/file.jpg""http://127.0.0.1/uploads/file.jpg"而不是"http://YOUR-SERVER-IP-ADDRESS/uploads/file.jpg"

In these cases, the actual device wont be able to locate the image.在这些情况下,实际设备将无法定位图像。 This only works with iOS Simulator because the virtual device is the same as the server and development machine.这仅适用于 iOS 模拟器,因为虚拟设备与服务器和开发机器相同。 It can read LOCALHOST and 127.0.0.1 .它可以读取LOCALHOST127.0.0.1

In my server, I was using a Rich Text Editor (TinyMCE) and it automatically removes the IP address after it detects that it's the same source.在我的服务器中,我使用的是富文本编辑器 (TinyMCE),它在检测到 IP 地址是同一来源后会自动删除 IP 地址。

WKWebView can load image or css file from NSTemporaryDirectory, so you can copy your files to NSTemporaryDirectory, and then load it. WKWebView 可以从 NSTemporaryDirectory 加载图片或 css 文件,所以你可以将你的文件复制到 NSTemporaryDirectory,然后加载它。 It works for me on iOS 14!它在 iOS 14 上对我有用! see this issue.看到这个问题。 ios-wkwebview-loadhtmlstring-baseurl-fails-to-load-images-and-read-css ios-wkwebview-loadhtmlstring-baseurl-fails-to-load-images-and-read-css

It took me a while to figure this out, but based on this answer I got it working:我花了一段时间才弄明白,但基于这个答案,我让它工作了:

https://stackoverflow.com/a/73519282/5868066 https://stackoverflow.com/a/73519282/5868066

Try this:尝试这个:

let htmlPath = URL(fileURLWithPath: "")
let htmlDirectory = htmlPath.deletingLastPathComponent() 
let htmlString = try! String(contentsOfFile: htmlPath.path, encoding: .utf8) 

let baseURL = URL(fileURLWithPath: htmlDirectory)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[.zero]

webView.loadFileURL(htmlPath, allowingReadAccessTo: documentsDirectory)
webView.loadHTMLString(htmlString, baseURL: baseURL)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM