使用 REST API for iOS Swift 3 从 AZURE 下载文件



我想使用 REST API 从 azure 下载文件,我为 iOS Swift>3 编写了以下代码,但是当我运行下载任务时,收到此错误:

InvalidHeaderValue其中一个 HTTP 标头的值格式不正确。 请求 Id:10d9c8f8-001a-00db-283c-1ab1d1000000 时间:2017-08-21T05:14:18.2768791Zx-ms-version

private let account = "myAccount"
private let key = "My key is encrypted as base64"
private let fileName = "My file name which have to download"
private let SHARE_NAME = "My share name"
let date  = Date().currentTimeZoneDate() + " GMT"
func downloadFileFromAzure(fileName:String)
{         
// Create destination URL 
let documentsUrl:URL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent(fileName)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://(account).file.core.windows.net/(SHARE_NAME)/(fileName)")!
//create session
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
//create headers field
var request = URLRequest(url:fileURL)
request.setValue(date,forHTTPHeaderField: "x-ms-date")
request.setValue("2014-02-14", forHTTPHeaderField: "x-ms-version")
request.setValue("(getFileRequest(account:account, fileName: fileName))", forHTTPHeaderField: "Authorization")
//download files
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: (statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
print("tempLocalUrl: (tempLocalUrl)")
print("destinationFileUrl: (destinationFileUrl)")
//reading
do {
let text = try String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print("reading files data : (text)")
}
catch (let writeError){ 
print("Error reading a file (writeError)")
}
} catch (let writeError) {
print("Error creating a file (destinationFileUrl) : (writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %@", error?.localizedDescription);
}
}
task.resume()
}

public func getFileRequest(account:String,fileName:String)->String
{        
let canonicalizedResources = "/(account)/(SHARE_NAME)/(fileName)"
let  canonicalizedHeaders = "x-ms-date:(date)nx-ms-version:2014-02-14"
let stringToSign = "GETnnnnnnnnnnnn(canonicalizedHeaders)n(canonicalizedResources)"
let auth = getAuthenticationString(stringToSign:stringToSign);
return auth
}
///getAuthenticationString
public func getAuthenticationString(stringToSign:String)->String
{                              
let authKey: String = stringToSign.hmac(algorithm: HMACAlgorithm.SHA256, key:  key)
let auth = "SharedKey " + account + ":" + authKey;
return auth;
}

enum HMACAlgorithm {

病例MD5, SHA1, SHA224,SHA256, SHA384, SHA512;

func toCCHmacAlgorithm() -> CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5:
result = kCCHmacAlgMD5
case .SHA1:
result = kCCHmacAlgSHA1
case .SHA224:
result = kCCHmacAlgSHA224
case .SHA256:
result = kCCHmacAlgSHA256
case .SHA384:
result = kCCHmacAlgSHA384
case .SHA512:
result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
func digestLength() -> Int {
var result: CInt = 0
switch self {
case .MD5:
result = CC_MD5_DIGEST_LENGTH
case .SHA1:
result = CC_SHA1_DIGEST_LENGTH
case .SHA224:
result = CC_SHA224_DIGEST_LENGTH
case .SHA256:
result = CC_SHA256_DIGEST_LENGTH
case .SHA384:
result = CC_SHA384_DIGEST_LENGTH
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}

}

extension String {
func hmac(algorithm: HMACAlgorithm, key: String) -> String {
let keyBytes = key.base64DecodeAsData() 
let dataBytes = self.cString(using: String.Encoding.utf8)
var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
CCHmac(algorithm.toCCHmacAlgorithm(), keyBytes.bytes, keyBytes.length, dataBytes!, Int(strlen(dataBytes!)), &result)
let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)
return String(hmacBase64)
} 
func base64DecodeAsData() -> NSData {
let decodedData = NSData(base64Encoded: self, options: NSData.Base64DecodingOptions(rawValue: 0))
return decodedData!
}

编辑:

我只在Xcode中收到此错误。

InvalidHeaderValue其中一个 HTTP 标头的值格式不正确。 请求 Id:10d9c8f8-001a-00db-283c-1ab1d1000000 时间:2017-08-21T05:14:18.2768791Zx-ms-version

但是当我在安卓源代码中尝试共享密钥(在 ios 中生成(时出现此错误:

身份验证失败的服务器无法对请求进行身份验证。确保授权标头的值格式正确,包括签名.equestId:379c5c1b-001a-0017-1a19-1bd564000000ime:2017-08-22T07:38:04.8712051Z在HTTP请求'tJcl9LyzF2BzlZMdW9ULtMojDamn9HnEY9LulpDOsYg='中找到的MAC签名与任何计算签名不同。服务器使用以下字符串进行签名:"GETx-ms-date:星期二, 22 Aug 2017 07:32:52 GMT-ms-version:2014-02-14account/SHARE_NAME/fileName"。

已解决

更改此行

let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)

自:

let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0((

还要更改此行

let stringToSign = "GET\(canonicalizedHeaders((canonicalizedResources(">

自:

let stringToSign = "GET" + ""//内容编码 + ""//内容语言 + ""//内容长度 + ""//内容 md5 + ""//内容类型 + ""//日期 + ""//如果修改自 + ""//如果匹配 + ""//如果不匹配 + ""//如果未修改,则自 + ""//范围 + "(canonicalizedHeaders("//headers + "(规范化资源("//资源

请先删除您存储的文件,然后再通过以下功能创建新文件:

func deleteFileFromDirectory(fileNameToDelete:String)
{     
var filePath = ""
// Fine documents directory on device
let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
if dirs.count > 0 {
let dir = dirs[0] //documents directory
filePath = dir.appendingFormat("/" + fileNameToDelete)
print("Local path = (filePath)")
} else {
print("Could not find local directory to store file")
return
}

do {
let fileManager = FileManager.default
// Check if file exists
if fileManager.fileExists(atPath: filePath) {
// Delete file
try fileManager.removeItem(atPath: filePath)
} else {
print("File does not exist")
}
}
catch let error as NSError {
print("An error took place: (error)")
}
}

我相信问题出在这 2 行代码中:

let  canonicalizedHeaders = "x-ms-date:(date)nx-ms-version:2016-05-31n"
let stringToSign = "GETnnnnnnnnnnnn(canonicalizedHeaders)n(canonicalizedResources)"

我注意到有一个额外的新行字符(n(。你能在canonicalizedHeaders末尾或(canonicalizedHeaders)(canonicalizedResources)之间删除stringToSignn字符吗?