Swift 3 Azure Blob Storage Data (Image, Video) upload with SAS - ios

I'm searching for an useful Swift 3 Azure Blob Storage example which I could use to upload some data(image, video). For now, I can insert records into my Mobile Service database and there I generate a SAS and I get it back to my iOS application. Now I need to know how to upload to Azure Blob Storage with help of that SAS. I successfully implemented the same for Android and it works, but somehow I have troubles to find any useful information for "SWIFT" and how to use the "SAS"!
Any code examples how to upload with SAS in Swift are much appreciated.
Regards,
Adam

For those who have the same problem as I had: This is a working example in Xcode 8 and Swift 3. You have to include the "Azure Storage Client Library" into your project.
//Upload to Azure Blob Storage with help of SAS
func uploadBlobSAS(container: String, sas: String, blockname: String, fromfile: String ){
// If using a SAS token, fill it in here. If using Shared Key access, comment out the following line.
var containerURL = "https://yourblobstorage.blob.core.windows.net/\(container)\(sas)" //here we have to append sas string: + sas
print("containerURL with SAS: \(containerURL) ")
var container : AZSCloudBlobContainer
var error: NSError?
container = AZSCloudBlobContainer(url: NSURL(string: containerURL)! as URL, error: &error)
if ((error) != nil) {
print("Error in creating blob container object. Error code = %ld, error domain = %#, error userinfo = %#", error!.code, error!.domain, error!.userInfo);
}
else {
let blob = container.blockBlobReference(fromName: blockname)
blob.uploadFromFile(withPath: fromfile, completionHandler: {(NSError) -> Void in
NSLog("Ok, uploaded !")
})
}
}

Related

iOS Swift: Firebase Storage upload error - only the initial file upload works

Environment:
Xcode 12.0.1 (Swift 5.x)
iOS 13
Firebase 6.34.0
FirebaseFirestore 1.19.0
FirebaseStorage 3.9.1
GoogleDataTransport 7.5.1
PromisesObjC 1.2.11
Problem:
I initialize Firebase in iOS app and successfully write database data to Cloud FireStore.
I then upload related video file to Firebase Storage with an asynch call.
Cloud Firestore database writes ALWAYS work.
Using the index generated from Cloud Firestore write, Firebase storage is then used to upload a video and a data file with names as the index from (1) above.
The first first file upload always works with a new app launch.
The second or any additional file upload fails with the following error:
cloud storage VIDEO file upload error: Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={object=PBY7Ost7nPWD8jWWF4qG.mov, ResponseBody=Can not finalize upload. Current size is 1692167. Expected final size is 1665242., bucket=launch-me-47860.appspot.com, data={length = 83, bytes = 0x43616e20 6e6f7420 66696e61 6c697a65 ... 31363635 3234322e }, data_content_type=text/plain; charset=utf-8, NSLocalizedDescription=An unknown error occurred, please check the server response., ResponseErrorDomain=com.google.HTTPStatus, ResponseErrorCode=400}
I have found similar questions posted from 2016 and 2017 but this Firebase write problem seems different as the initial upload always works and then the next time I try to perform an upload it fails with the error.
I added a routine to try additional upload attempts if the first upload fails. They all fail.
Here is the upload code:
// MARK: Write file to Firebase Cloud Storage
private func fbCloudFileWrite(indexName: String) {
let fbStorage = Storage.storage()
print("(DEBUG FB) fbCloudFileWrite: upload indexName.csv and indexName.mov")
// now upload file to cloud FireStore
let fbStorageRef = fbStorage.reference()
// Create a reference to the file you want to upload
//let LaunchMeDataRef = fbStorageRef.child("LaunchMe/" + indexName + ".csv")
//let LaunchMeVideoRef = fbStorageRef.child("LaunchMe/" + indexName + ".mov")
var LaunchMeDataRef = fbStorageRef.child(indexName + ".csv")
var LaunchMeVideoRef = fbStorageRef.child(indexName + ".mov")
// Upload the file to the path "images/rivers.jpg"
fbWriteAttempts += 1
DispatchQueue.main.async {
print("(DEBUG FB) ***** write attemp #: \(self.fbWriteAttempts)")
if let vURL = self.videoURL, let dURL = self.dataFileURL {
let uploadVideoTask = LaunchMeVideoRef.putFile(from: vURL, metadata: nil) { metadata, err in
if let err = err {
print("(DEBUG FB) cloud storage VIDEO file upload error: \(err)")
if self.fbWriteAttempts < 4 {
self.fbCloudFileWrite(indexName: indexName)
}
} else {
print("(DEBUG FB) video uploaded: \(indexName)")
let uploadDataTask = LaunchMeDataRef.putFile(from: dURL, metadata: nil) { metadata, err in
if let err = err {
print("(DEBUG FB) cloud storage SENSOR file upload error: \(err)")
} else {
print("(DEBUG FB) sensor data uploaded: \(indexName)")
print("(DEBUG FB) set newRecording = false to prevent duplicates")
// all files successfully uploaded. Set newRecording to false
self.newRecording = false
}
} // close uploadDataTask
} // close else
} // close let uploadVideoTask
} // close vURL unwrap
else {
print("(DEBUG FB) videoURL could not be unwrapped")
}
} // close Dispatch.main.async
Strangely enough, I was able to solve this problem by setting the URL variable of the movie local directory path within the VideoPlayerViewController instead of passing the URL in from the previous controller during the segue. This makes no sense to me that this would be necessary except perhaps some caching that happens behind the scenes.
I know the correct URL is passed in as the correct video plays on the screen when it is passed in, yet the Firebase Storage upload tries to upload the previous video (hence the expected size error) when provided the same URL that played the correct video.
New function added within the controller that sets the URL instead of passing it in (prior to performing the Firebase Storage upload):
private func setVideoStorageURL() {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let path = "output.mov"
videoURL = paths[0].appendingPathComponent(path)
}
whereas the previous implementation that was not working passed the URL into the controller:
if let destinationVC = segue.destination as? VideoPlayerViewController {
print("(DEBUG) Setting data to be passed to VideoPlayerViewController")
destinationVC.newRecording = newRecording
//now set newRecording to false if it is true
if newRecording {
newRecording = false
}
destinationVC.audioMode = audioMode
destinationVC.selectedDevice = selectedDevice
destinationVC.videoURL = passFileURL

Process for uploading image to s3 with AWS Appsync || iOS image uploading with Appsync

I'm working on a new project that requires uploading attachments in the form of images. I'm using DynamoDB and AppSync API's to insert and retrieve data from database. As we are new to the AppSync and all the amazon services and database we are using for the app i'm little bit confused about the authentication process. Right now we are using API key for authentication and I have tried these steps to upload image to s3.
1 Configue the AWSServiceManager with static configuration like :-
let staticCredit = AWSStaticCredentialsProvider(accessKey: kAppSyncAccessKey, secretKey: kAppSyncSecretKey)
let AppSyncRegion: AWSRegionType = .USEast2
let config = AWSServiceConfiguration(region: AppSyncRegion, credentialsProvider: staticCredit)
AWSServiceManager.default().defaultServiceConfiguration = config
2 Uploading picture with this method : -
func updatePictureToServer(url:URL, completion:#escaping (Bool)->Void){
let transferManager = AWSS3TransferManager.default()
let uploadingFileURL = url
let uploadRequest = AWSS3TransferManagerUploadRequest()
let userBucket = String(format: "BUCKET")
uploadRequest?.bucket = userBucket
let fileName = String(format: "%#%#", AppSettings.getUserId(),".jpg")
uploadRequest?.key = fileName
uploadRequest?.body = uploadingFileURL
transferManager.upload(uploadRequest!).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask<AnyObject>) -> Any? in
if let error = task.error as NSError? {
if error.domain == AWSS3TransferManagerErrorDomain, let code = AWSS3TransferManagerErrorType(rawValue: error.code) {
switch code {
case .cancelled, .paused:
break
default:
print("Error uploading: \(String(describing: uploadRequest!.key)) Error: \(error)")
}
} else {
print("Error uploading: \(String(describing: uploadRequest!.key)) Error: \(error)")
}
completion(false)
return nil
}
_ = task.result
completion(true)
print("Upload complete for: \(String(describing: uploadRequest!.key))")
return nil
})
}
3 And finally i'm able to see the uploaded image on the S3 bucket
But i'm concerned about how to save the url of the image and how to retrieve the image because when i have to make the buket PUBLIC to retrieve the image and i don't think that's a good approach, plus is it necessary to have a Cognito user pool because we aren't using Cognito user pool yet in our app and not have much knowledge about that too and documents are not helping in practical situations because we are implementing ti for the first time so we need some little help.
So two question : -
Proper procedure to use for uploading and retrieving images for S3 and AppSync.
Is it necessary to use Cognito user pool for image uploading and retrieving.
Thanks
Note: Any suggestion or improvement or anything related to the AppSync, S3 or DynamoDB will be truly appreciated and language is not a barrier just looking for directions so swift or objective-c no problem.
You need per-identity security on the bucket using Cognito Federated Identities which gives each user their own secure bucket. You can leverage the AWS Amplify to set this up for your project with $amplify add auth and selecting the default config, then $amplify add storage which configures that bucket and pool with appropriate permissions to use private uploads.
For more info checkout the repo: https://github.com/aws-amplify/amplify-cli

How to upload a video from iOS photo album to Azure Blob Storage

I am struggling with uploading videos from iOS photo album to Azure blob storage. I am using AZSClient.
uploading images is straight forward, ie. I get the image 'Data' from PHAsset and then upload it to azure storage using AZSCloudBlockBlob.uploadFromData method
Can anyone guide me on how to upload a video to azure blob preferably in swift
There was a similar thread for this they used the bellow code, and they used the IOS library found here:
//Upload to Azure Blob Storage with help of SAS
func uploadBlobSAS(container: String, sas: String, blockname: String, fromfile: String ){
// If using a SAS token, fill it in here. If using Shared Key access, comment out the following line.
var containerURL = "https://yourblobstorage.blob.core.windows.net/\(container)\(sas)" //here we have to append sas string: + sas
print("containerURL with SAS: \(containerURL) ")
var container : AZSCloudBlobContainer
var error: NSError?
container = AZSCloudBlobContainer(url: NSURL(string: containerURL)! as URL, error: &error)
if ((error) != nil) {
print("Error in creating blob container object. Error code = %ld, error domain = %#, error userinfo = %#", error!.code, error!.domain, error!.userInfo);
}
else {
let blob = container.blockBlobReference(fromName: blockname)
blob.uploadFromFile(withPath: fromfile, completionHandler: {(NSError) -> Void in
NSLog("Ok, uploaded !")
})
}
}
I found the answer in this thread
let manager = PHImageManager.default()
manager.requestAVAsset(forVideo: asset, options: nil, resultHandler: { (avasset, audio, info) in
if let avassetURL = avasset as? AVURLAsset {
guard let video = try? Data(contentsOf: avassetURL.url) else {
return
}
videoData = video
}
})
once you get the Data object then you can use AZSCloudBlockBlob.uploadFromData to upload it to azure storage

How to save PDF files from Firebase Storage into App Documents for future use?

I have connected my App with the Firebase Storage where my 19ea PDF files exists.
I would like to download those files and save them locally for future use.
Those PDF files will be used inside UIWebviews but they may need to be updated in time. Therefore, I have configured version control system with Firebase Database, so I will be able to push the newer versions when I update the files in the storage.
So, how I can save those files locally? (to a folder like: user/myapp/Documents/PDF etc?)
Also, how I can check if that folder contains any documents and how to delete them before downloading new files?
Here is what I have got so far.
I appreciate all the help.
// Firebase Storage Connection
static var refStorage:FIRStorageReference?
static var dataPDF = [NSData]()
func newDataDownload(){
// Compare Current Data Version with Online Data Version
if myFirebaseData.localDataVersion < myFirebaseData.onlineDataVersion {
// Set Firebase Storage Reference
myFirebaseData.refStorage = FIRStorage.storage().reference()
for i in 1...myFirebaseData.onlineTotalPDFCount {
// Create a reference to the file you want to download
let pulledPDF = FIRStorage.storage().reference().child("/PDF/\(i).pdf")
// Create local filesystem URL
let localURL = URL(string: "myApp/Documents/PDF/\(i)")!
pulledPDF.data(withMaxSize: myFirebaseData.maxPDFdownloadSize, completion: { (downPDF, err) in
if err == nil {
// Accessed the data
myFirebaseData.dataPDF.append(downPDF! as NSData)
print(myFirebaseData.dataPDF)
} else {
// If there is an error print it
print(err.debugDescription)
}
})
}
}
// If Data is successfully downloaded update Local Data Version
myFirebaseData.localDataVersion = myFirebaseData.onlineDataVersion
Use storageRef.write(toFile: completion:) (docs), like:
// Create a reference to the file you want to download
let pdfRef = storageRef.child("files/file.pdf")
// Create local filesystem URL
let localURL = URL(string: "path/to/local/file.pdf")!
// Download to the local filesystem
let downloadTask = pdfRef.write(toFile: localURL) { url, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Local file URL for "path/to/local/file.pdf" is returned
}
}
Note that you can only write to /tmp and /Documents due to app sandboxing requirements (see Downloading Firebase Storage Files Device Issue for an example of how this fails otherwise).

How do I store private app data on Google Drive with my iOS app?

I have an iOS app that has a local database. I'd like to back that up for users who choose to sign in with Google. The web (https://developers.google.com/drive/web/appdata) and android (https://developers.google.com/drive/android/appfolder) have guides on how to do this, but I can't find a similar one for iOS. Does it exist?
If you already have code to upload a file to the user's Drive account, it is very easy to switch to uploading into the private app folder instead. When making the Files.insert call, the file will be added to all of the folders listed in the parents[] array. (If this array is empty, by default the file is added to the root folder.) To upload the file into the private app data folder, simply set the parents[] array to appfolder. You have to do this at the same time as uploading the file, because once it has been uploaded the file can't be moved between the user's drive and your app's private data folder.
(Note: you may need to use the regular REST API to do this, because Google's Drive API for iOS docs do not show any methods for actually uploading a new file to Drive.)
Check this how this is working for me in swift 4.2 and above:
let googleDrive: GTLRDrive_File = GTLRDrive_File()
googleDrive.name = "name.json"
googleDrive.parents = ["appDataFolder"]
let uploadParam: GTLRUploadParameters = GTLRUploadParameters(data: data, mimeType: "application/json")
uploadParameters.shouldUploadWithSingleRequest = true;
let queryDrive: GTLRDriveQuery_FilesCreate = GTLRDriveQuery_FilesCreate.query(withObject: metadata, uploadParameters: uploadParam)
queryDrive.fields = "id"
self.service.executeQuery(queryDrive) { (result, response, error) in
if let file = response as? GTLRDrive_File {
if (error == nil) {
print(file.identifier)
/// your code here
} else {
// handle error part
}
}
else {
//handle exception part
}
}
"data" the json data you get this like below
let param = [["key": "value"], ["key": "value"], ["key": "value"]]
let data = try JSONSerialization.data(withJSONObject: param, options: .prettyPrinted)

Resources