Skip to content

Commit c8ebe26

Browse files
author
Arik
committed
Merge pr ionic-team#692 Support Range requests for reading local files
1 parent 871f521 commit c8ebe26

File tree

1 file changed

+73
-26
lines changed

1 file changed

+73
-26
lines changed

src/ios/IONAssetHandler.m

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@ - (instancetype)initWithBasePath:(NSString *)basePath andScheme:(NSString *)sche
1717
return self;
1818
}
1919

20-
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
21-
{
22-
NSString * startPath = @"";
23-
NSURL * url = urlSchemeTask.request.URL;
24-
NSString * stringToLoad = url.path;
25-
NSString * scheme = url.scheme;
26-
20+
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask {
21+
NSString * startPath = @""; // Initialize startPath
22+
NSURL * url = urlSchemeTask.request.URL; // Extract URL from the request
23+
NSString * stringToLoad = url.path; // Get the path component of the URL
24+
NSString * scheme = url.scheme; // Get the scheme of the URL
25+
// Check if the URL's scheme matches the custom scheme
2726
if ([scheme isEqualToString:self.scheme]) {
27+
// If the path starts with "/_app_file_", strip this prefix
2828
if ([stringToLoad hasPrefix:@"/_app_file_"]) {
2929
startPath = [stringToLoad stringByReplacingOccurrencesOfString:@"/_app_file_" withString:@""];
3030
} else {
31+
// Otherwise, build the path relative to self.basePath
3132
startPath = self.basePath ? self.basePath : @"";
3233
if ([stringToLoad isEqualToString:@""] || [url.pathExtension isEqualToString:@""]) {
3334
startPath = [startPath stringByAppendingString:@"/index.html"];
@@ -38,28 +39,74 @@ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)ur
3839
}
3940
NSError * fileError = nil;
4041
NSData * data = nil;
41-
if ([self isMediaExtension:url.pathExtension]) {
42-
data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError];
43-
}
44-
if (!data || fileError) {
45-
data = [[NSData alloc] initWithContentsOfFile:startPath];
46-
}
47-
NSInteger statusCode = 200;
48-
if (!data) {
49-
statusCode = 404;
42+
NSString * mimeType = [self getMimeType:url.pathExtension]; // Get MIME type based on file extension
43+
NSInteger statusCode = 200; // Default to 200 OK status
44+
NSURL * localUrl = [NSURL URLWithString:url.absoluteString]; // Create a URL object
45+
id response = nil;
46+
47+
NSFileManager *fileManager = [NSFileManager defaultManager];
48+
if (![fileManager fileExistsAtPath:startPath]) {
49+
// File not found, return 404
50+
statusCode = 404;
51+
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:@{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache" }];
52+
[urlSchemeTask didReceiveResponse:response];
53+
[urlSchemeTask didFinish];
54+
return;
55+
}
56+
57+
// Check for Range header
58+
NSString *rangeHeader = [urlSchemeTask.request valueForHTTPHeaderField:@"Range"];
59+
NSUInteger fileLength = (NSUInteger)[[fileManager attributesOfItemAtPath:startPath error:nil] fileSize];
60+
61+
if (rangeHeader) {
62+
NSRange range = NSMakeRange(NSNotFound, 0);
63+
if ([rangeHeader hasPrefix:@"bytes="]) {
64+
NSString *byteRange = [rangeHeader substringFromIndex:6];
65+
NSArray<NSString *> *rangeParts = [byteRange componentsSeparatedByString:@"-"];
66+
NSUInteger start = (NSUInteger)[rangeParts[0] integerValue];
67+
NSUInteger end = rangeParts.count > 1 && ![rangeParts[1] isEqualToString:@""] ? (NSUInteger)[rangeParts[1] integerValue] : fileLength - 1;
68+
range = NSMakeRange(start, end - start + 1);
69+
}
70+
71+
if (range.location != NSNotFound) {
72+
// Ensure range is valid
73+
if (range.location >= fileLength) {
74+
statusCode = 416; // Requested Range Not Satisfiable
75+
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:@{ @"Content-Type" : mimeType, @"Content-Range": [NSString stringWithFormat:@"bytes */%lu", (unsigned long)fileLength], @"Cache-Control": @"no-cache" }];
76+
[urlSchemeTask didReceiveResponse:response];
77+
[urlSchemeTask didFinish];
78+
return;
79+
}
80+
81+
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:startPath];
82+
[fileHandle seekToFileOffset:range.location];
83+
data = [fileHandle readDataOfLength:range.length];
84+
[fileHandle closeFile];
85+
86+
statusCode = 206; // Partial Content
87+
NSString *contentRange = [NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)range.location, (unsigned long)(range.location + range.length - 1), (unsigned long)fileLength];
88+
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:@{ @"Content-Type" : mimeType, @"Content-Range": contentRange, @"Content-Length": [NSString stringWithFormat:@"%lu", (unsigned long)range.length], @"Cache-Control": @"no-cache" }];
89+
}
5090
}
51-
NSURL * localUrl = [NSURL URLWithString:url.absoluteString];
52-
NSString * mimeType = [self getMimeType:url.pathExtension];
53-
id response = nil;
54-
if (data && [self isMediaExtension:url.pathExtension]) {
55-
response = [[NSURLResponse alloc] initWithURL:localUrl MIMEType:mimeType expectedContentLength:data.length textEncodingName:nil];
56-
} else {
57-
NSDictionary * headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache"};
91+
92+
if (!response) {
93+
// Load entire file if no range is requested
94+
data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError];
95+
if (!data || fileError) {
96+
data = [[NSData alloc] initWithContentsOfFile:startPath];
97+
}
98+
99+
if (!data) {
100+
statusCode = 404;
101+
}
102+
103+
NSDictionary *headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache" };
58104
response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:headers];
59-
}
60-
105+
} // Send response and data to the WKWebView
61106
[urlSchemeTask didReceiveResponse:response];
62-
[urlSchemeTask didReceiveData:data];
107+
if (data) {
108+
[urlSchemeTask didReceiveData:data];
109+
}
63110
[urlSchemeTask didFinish];
64111

65112
}

0 commit comments

Comments
 (0)