我们有一个使用PhoneGap / Cordova 4.3.0构建的iOS应用程序。此应用使用<content src="http://example.com/foo" />
文件中的config.xml
直接加载外部网站。所有功能都包含在本网站中,因此我们实际上并没有使用任何本地HTML或JS文件。
作为应用功能的一部分,我们必须播放一些视频。由于该应用程序也设计为脱机工作,因此我们希望在本地缓存这些视频。因此,我们使用FileTransfer插件将其下载到设备,以及其他资源,如图像或PDF。下载文件后,我们将获得具有file://
协议的URL。我们还可以选择使用cdvfile://
协议。当我们使用cdvfile://
网址显示图片时,图片会正确显示。但是,视频根本不播放。
要播放视频,我们使用的是标准HTML5视频标记:
<video width="auto" height="100%" controls="controls" autoplay="true">
<source src="..." type="video/mp4" />
</video>
视频本身正在运行,它们将从外部源正确播放(如果我们从服务器而不是本地文件系统访问它们,它们将播放)。我意识到问题与Web相关的概念有关,例如同源策略和访问本地文件系统的限制。然而,与此同时,我必须想知道为什么图像在这些相同的约束条件下工作正常。
到目前为止我尝试过:
file://
和cdvfile://
个网址作为视频的src
。这不会产生任何视觉效果。屏幕简直是黑色。iframe
src
设置为视频网址。使用file://
时,屏幕仍为黑色。但是,使用cdvfile://
时,会出现iOS视频播放器界面,其中包含播放按钮和全屏按钮,但视频无法播放,也没有时间轴。video.html
的cordova中,该文件将URL作为参数,并将video
标记与该网址src
一起呈现。计划是将此文件包含为iframe
,但显然我无法对本地文件进行iframe
。我尝试过各种可能指向特定video.html
文件的URL(虽然实际上我不确定这是否可行)。我尝试过的是:cordova.file.applicationDirectory + 'www/video.html'
,http://localhost/www/video.html
,cdvfile://localhost/www/video.html
。现在,我可能会以错误的方式解决这个问题。正如我所看到的,cordova的“标准用例”是您在本地存储HTML / JS / CSS文件。像我正在使用的外部内容可能有点不寻常。我将解释这个使我使用此功能的应用程序的要求。
cache.manifest
,允许我们控制对Web应用程序代码的更新,同时允许它在本地缓存。这可能是最重要的考虑因素,因为如果我们想在Cordova中本地保存一些文件,我们就必须在Javascript中重新实现这个缓存功能(使用尽可能薄的层)。无论如何,我主要担心的是如何让这些视频正常运作。我愿意尝试最骇客的解决方法!如果当前的开发决策真的不可能,那么也许你可以给我一些关于如何构建应用程序的提示,以使其工作并仍然满足我的要求。
非常感谢!
答案 0 :(得分:4)
一年前我有一个类似的项目。但是我不记得遇到这个问题,因为我们将html / js / css资产与应用程序捆绑在一起。
问题是您正在尝试从http:///协议提供的html文件加载file:/// protocol url,这不是本机UIWebView所熟悉的。
您可以使用自定义URL方案(例如video://)绕过此方法,但您需要编写一些拦截此请求的本机代码,并将实际视频传输回URL加载系统。
最终结果:
以下是我使用Cordova 4.3.0&amp;一点ObjectiveC
VideoURLProtocol.h:
#import <Foundation/Foundation.h>
@interface VideoURLProtocol : NSURLProtocol <NSURLConnectionDelegate>
@property (strong, nonatomic) NSURLConnection *connection;
@end
VideoURLProtocol.m:
#import "VideoURLProtocol.h"
@implementation VideoURLProtocol
@synthesize connection;
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
return [[[request URL] absoluteString] rangeOfString:@"video://"].location != NSNotFound;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
- (void)startLoading {
NSString *currentURL = [[self.request URL] absoluteString];
NSString *newURL = [currentURL stringByReplacingOccurrencesOfString:@"video://" withString:@"file:///"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:newURL]];
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)stopLoading {
[self.connection cancel];
self.connection = nil;
}
@end
将以下行添加到AppDelegate.m的didFinishLaunchingWithOptions方法
.
.
// These lines should already be there
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
// This line
[NSURLProtocol registerClass:[VideoURLProtocol class]];
.
.
以下是使用此新网址方案的JavaScript代码
document.addEventListener('deviceready', function(){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){
var caches = fileSystem.root.nativeURL.replace("Documents", "Library/Caches"),
videoPath = caches + "video.mp4";
new FileTransfer().download("http://clips.vorwaerts-gmbh.de/VfE_html5.mp4", videoPath, function(entry){
document.getElementsByTagName("video")[0].src = caches.replace("file:///", "video://") + "video.mp4"
}, function(error){
alert("unable to download file: " + error);
});
});
}, false);
值得一提的其他一些要点:
请注意,在我的javascript代码中,我将文件下载到&#34; / Library / Caches&#34;而不是&#34; / Documents&#34;目录(默认位置)这是因为&#34; / Documents&#34;目录备份到iCloud&amp; Apple拒绝尝试备份超过100 MB的应用程序。在我的应用程序遭到拒绝后,我发现了这一点。 您可以在以下位置查看应用占用的空间:设置&gt; iCloud&gt;存储&gt;管理存储&gt; {{您的iphone名称}}&gt;显示所有应用
您可以通过在config.xml中添加以下行来自动播放视频
<preference name="MediaPlaybackRequiresUserAction" value="false" />
您还可以通过在config.xml中添加以下行来内联播放视频,此外还需要添加webkit-playsinline =&#34; true&#34;属于您的视频:
<preference name="AllowInlineMediaPlayback" value="true" />
<video controls="controls" autoplay="true" webkit-playsinline="true" preload="auto">
</video>
这是Ray的一个非常有趣的教程,非常详细地解释了NSURLProtocol:http://www.raywenderlich.com/59982/nsurlprotocol-tutorial
答案 1 :(得分:0)
根据我使用file://
协议的经验在iOS上存在问题,因为协议从设备文件系统的根开始。
我不相信这里遇到任何跨源问题,因为Cordova没有实现跨源请求,而是将所有请求视为来自他们请求的来源。 See this answer
我的理论解决方案是使用相对网址,而不是尝试使用任何协议。但是,实现此方法可能取决于文件成功下载的时间。
<video width="auto" height="100%" controls="controls" autoplay="true">
<source src="/localhost/www/video.mp4" type="video/mp4" />
</video>
cdvfile://localhost/www/
是您在调用target
referenced here时为fileTransfer.download()
参数设置的路径。
successCallback
触发后,可能需要创建视频元素或在javascript中设置视频src。您再次将src设置为相对URL。
请注意视频不会在移动设备上自动播放
From the Safari Developer Library
在iOS上的Safari(适用于所有设备,包括iPad)中,用户可能位于移动网络上并按数据单元收费,因此禁用预加载和自动播放。