我有这个代码从本地目录读取epub文件并向用户显示电子书。我希望能够从远程位置读取epub文件。有人可以指出我如何做到这一点的正确方向?我需要更改的功能是 readEpub 以及它调用的函数。谢谢!
/**
Unzip, delete and read an epub file.
Returns a FRBook.
*/
func readEpub(epubPath withEpubPath: String, removeEpub: Bool = true, unzipPath: String? = nil) throws -> FRBook? {
epubPathToRemove = withEpubPath
shouldRemoveEpub = removeEpub
var isDir: ObjCBool = false
let fileManager = FileManager.default
let bookName = (withEpubPath as NSString).lastPathComponent
if let path = unzipPath, (fileManager.fileExists(atPath: path) == true) {
bookBasePath = path
} else {
bookBasePath = kApplicationDocumentsDirectory
}
bookBasePath = (bookBasePath as NSString).appendingPathComponent(bookName)
guard fileManager.fileExists(atPath: withEpubPath) else {
throw FolioReaderError(kind: .BookNotAvailable)
}
// Unzip if necessary
var needsUnzip = false
if fileManager.fileExists(atPath: bookBasePath, isDirectory:&isDir) {
if !isDir.boolValue { needsUnzip = true }
} else {
needsUnzip = true
}
if needsUnzip {
SSZipArchive.unzipFile(atPath: withEpubPath, toDestination: bookBasePath, delegate: self)
}
// Skip from backup this folder
addSkipBackupAttributeToItemAtURL(URL(fileURLWithPath: bookBasePath, isDirectory: true))
self.book.name = bookName
try readContainer()
try readOpf()
return self.book
}
/**
Read and parse container.xml file.
*/
fileprivate func readContainer() throws {
let containerPath = "META-INF/container.xml"
do {
let containerData = try Data(contentsOf: URL(fileURLWithPath: (bookBasePath as NSString).appendingPathComponent(containerPath)), options: .alwaysMapped)
let xmlDoc = try AEXMLDocument(xml: containerData)
let opfResource = FRResource()
opfResource.href = xmlDoc.root["rootfiles"]["rootfile"].attributes["full-path"]
opfResource.mediaType = FRMediaType.determineMediaType(xmlDoc.root["rootfiles"]["rootfile"].attributes["full-path"]!)
book.opfResource = opfResource
resourcesBasePath = (bookBasePath as NSString).appendingPathComponent((book.opfResource.href as NSString).deletingLastPathComponent)
} catch {
throw FolioReaderError(kind : .ErrorInContainer)
}
}
/**
Read and parse .opf file.
*/
fileprivate func readOpf() throws {
let opfPath = (bookBasePath as NSString).appendingPathComponent(book.opfResource.href)
var identifier: String?
do {
let opfData = try Data(contentsOf: URL(fileURLWithPath: opfPath), options: .alwaysMapped)
let xmlDoc = try AEXMLDocument(xml: opfData)
// Base OPF info
if let package = xmlDoc.children.first {
identifier = package.attributes["unique-identifier"]
if let version = package.attributes["version"] {
book.version = Double(version)
}
}
// Parse and save each "manifest item"
for item in xmlDoc.root["manifest"]["item"].all! {
let resource = FRResource()
resource.id = item.attributes["id"]
resource.properties = item.attributes["properties"]
resource.href = item.attributes["href"]
resource.fullHref = (resourcesBasePath as NSString).appendingPathComponent(resource.href).removingPercentEncoding
resource.mediaType = FRMediaType.mediaTypeByName(item.attributes["media-type"]!, fileName: resource.href)
resource.mediaOverlay = item.attributes["media-overlay"]
// if a .smil file is listed in resources, go parse that file now and save it on book model
if (resource.mediaType != nil && resource.mediaType == FRMediaType.SMIL) {
readSmilFile(resource)
}
book.resources.add(resource)
}
book.smils.basePath = resourcesBasePath
// Read metadata
book.metadata = readMetadata(xmlDoc.root["metadata"].children)
// Read the book unique identifier
if let uniqueIdentifier = book.metadata.findIdentifierById(identifier) {
book.uniqueIdentifier = uniqueIdentifier
}
// Read the cover image
let coverImageId = book.metadata.findMetaByName("cover")
if let coverResource = book.resources.findById(coverImageId) {
book.coverImage = coverResource
} else if let coverResource = book.resources.findByProperty("cover-image") {
book.coverImage = coverResource
}
// Specific TOC for ePub 2 and 3
// Get the first resource with the NCX mediatype
if let tocResource = book.resources.findByMediaType(FRMediaType.NCX) {
book.tocResource = tocResource
} else if let tocResource = book.resources.findByExtension(FRMediaType.NCX.defaultExtension) {
// Non-standard books may use wrong mediatype, fallback with extension
book.tocResource = tocResource
} else if let tocResource = book.resources.findByProperty("nav") {
book.tocResource = tocResource
}
assert(book.tocResource != nil, "ERROR: Could not find table of contents resource. The book don't have a TOC resource.")
// The book TOC
book.tableOfContents = findTableOfContents()
book.flatTableOfContents = createFlatTOC()
// Read Spine
let spine = xmlDoc.root["spine"]
book.spine = readSpine(spine.children)
// Page progress direction `ltr` or `rtl`
if let pageProgressionDirection = spine.attributes["page-progression-direction"] {
book.spine.pageProgressionDirection = pageProgressionDirection
}
} catch {
throw FolioReaderError(kind: .ErrorInOpf)
}
}
/**
Reads and parses a .smil file
*/
fileprivate func readSmilFile(_ resource: FRResource) {
do {
let smilData = try Data(contentsOf: URL(fileURLWithPath: resource.fullHref), options: .alwaysMapped)
var smilFile = FRSmilFile(resource: resource)
let xmlDoc = try AEXMLDocument(xml: smilData)
let children = xmlDoc.root["body"].children
if children.count > 0 {
smilFile.data.append(contentsOf: readSmilFileElements(children))
}
book.smils.add(smilFile)
} catch {
print("Cannot read .smil file: "+resource.href)
}
}
fileprivate func readSmilFileElements(_ children:[AEXMLElement]) -> [FRSmilElement] {
var data = [FRSmilElement]()
// convert each smil element to a FRSmil object
for item in children {
let smil = FRSmilElement(name: item.name, attributes: item.attributes)
// if this element has children, convert them to objects too
if item.children.count > 0 {
smil.children.append(contentsOf: readSmilFileElements(item.children))
}
data.append(smil)
}
return data
}
/**
Read and parse the Table of Contents.
*/
fileprivate func findTableOfContents() -> [FRTocReference] {
var tableOfContent = [FRTocReference]()
var tocItems: [AEXMLElement]?
guard let tocResource = book.tocResource else { return tableOfContent }
let tocPath = (resourcesBasePath as NSString).appendingPathComponent(tocResource.href)
do {
if tocResource.mediaType == FRMediaType.NCX {
let ncxData = try Data(contentsOf: URL(fileURLWithPath: tocPath), options: .alwaysMapped)
let xmlDoc = try AEXMLDocument(xml: ncxData)
if let itemsList = xmlDoc.root["navMap"]["navPoint"].all {
tocItems = itemsList
}
} else {
let tocData = try Data(contentsOf: URL(fileURLWithPath: tocPath), options: .alwaysMapped)
let xmlDoc = try AEXMLDocument(xml: tocData)
if let nav = xmlDoc.root["body"]["nav"].first, let itemsList = nav["ol"]["li"].all {
tocItems = itemsList
} else if let nav = findNavTag(xmlDoc.root["body"]), let itemsList = nav["ol"]["li"].all {
tocItems = itemsList
}
}
} catch {
print("Cannot find Table of Contents.")
}
guard let items = tocItems else { return tableOfContent }
for item in items {
tableOfContent.append(readTOCReference(item))
}
return tableOfContent
}