Swift Vapor 4 upload , validate , resize an image file - swift

I am trying to post a photo to the vapor 4 server.
I am sending a Team name as a string and an image as data.
struct SendTeam: Content {
var name: String
var img: Data
}
I want to upload the photo after validating its size to be not more than 1MB, and mimetype is of type image like (jpg, jpeg, png), then resize that image to 300px*300px and finally save it to the public\uploads directory.
I am not able to figure out how to do that.
Here is my code.
func create(req: Request) async throws -> SendTeam {
let team = try req.content.decode(SendTeam.self)
let path = req.application.directory.publicDirectory + "originals/" + team.name + "-\(UUID())"
try await req.fileio.writeFile(.init(data: team.img), at: path)
if team.name.count < 4 || team.name.count > 20 {
throw Abort(.badRequest, reason: "wrong name")
}
return team
}
Code should work on ubuntu server VPS cloud instance as well.

After Two Days of Testing, I am able to do that using SwiftGD, So I came up with this .. hope it is useful.
Image Validation
// Do not forget to decode the image to File type Not Data type
let img = team.img
if img.data.readableBytes > 1000000 {
errors.append( "error ... image size should not exceed 1 mb")
}
if !["png", "jpeg", "jpg"].contains(img.extension?.lowercased()) {
errors.append("extension is not acceptable")
}
let imageNewNameAndExtension = "\(UUID())"+".\(img.extension!.lowercased())"
The upload an resize part
// The upload Path
let path = req.application.directory.publicDirectory + "uploads/" + imageNewNameAndExtension
// The path to save the resized img
let newPath = req.application.directory.publicDirectory + "uploads/teams/" + imageNewNameAndExtension
// SwiftNIO File handle
let handle = try await req.application.fileio.openFile(path: path,mode: .write,flags:.allowFileCreation(posixMode:0x744),eventLoop: req.eventLoop).get()
// Save the file to the server
req.application.fileio.write(fileHandle:handle,buffer:img.data,eventLoop: req.eventLoop).whenSuccess { _ in
// SwiftGD part to resize the image
let url = URL(fileURLWithPath: path)
let newUrl = URL(fileURLWithPath: newPath)
let image = Image(url: url)
if let im = image {
if let mg = im.resizedTo(width: 250, height: 250){
mg.write(to: newUrl)
}
}
try? handle.close()
}

Related

Swift : read .vcf file from document directory?

We get .vcf from Document directory path but can't import to the device using .vcf file URL like
'"Nik1 .vcf":
file:///Users/jksol-niketan/Library/Developer/CoreSimulator/Devices/18ADE140-22E4-4BEA-8C25-886AEE96C2CC/data/Containers/Data/Application/BC6A91A7-2920-4D5D-9787-F6D0E0DAB200/Documents/restore/Nik1%20.vcf'
How can solution for this file path URL to import iPhone device using swift/objective c
I tired of this issue, help with this query solution.
for r in restore{
var data: Data = Data()
do{
let url = URL(fileURLWithPath: r.value.path)
try data = NSData(contentsOf: url) as Data
var usersContact = [CNContact]()
do {
try usersContact = CNContactVCardSerialization.contacts(with: data)
} catch {
print("error")
}
let contact = usersContact[0]
print(contact)
}catch{
print("error")
}
}
Ex. restore = ["Nik1 .vcf":
file:///Users/jksol-niketan/Library/Developer/CoreSimulator/Devices/18ADE140-22E4-4BEA-8C25-886AEE96C2CC/data/Containers/Data/Application/BC6A91A7-2920-4D5D-9787-F6D0E0DAB200/Documents/restore/Nik1%20.vcf]

How to use image out of storage.reference(withPath: "PATH").getData method

I have been trying to use an image I downloaded from firebase out of the following method by creating an array and appending it to it. Whenever I try to use that image out of the line
if let data = data, let imge1 = UIImage(data: data) {
I am unable to see it or display it.
Is there a way of using the image outside of that function?
Here is my code to get the image:
let storage = Storage.storage()
let imageReference = storage.reference(withPath: "img/betos.png")
imageReference.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print("Could not retrieve profile image: \(error.localizedDescription)")
return
}
if let data = data, let imge1 = UIImage(data: data) {
//self.profileButton.setImage(profileImage, for: .normal)
print("success")
logoImages.append(imge1)
}
}
and here is how I try to use it:
let cellView = UIImageView()
cellView.image = logoImages.first
cellView.frame = CGRect(x:50, y: 50, width: 50, height: 50)
view.addSubview(cellView)
The General use of images is explained here for the web example:
https://firebase.google.com/docs/storage/ios/download-files
This is if you didn't generate a SignedUrl first.
In your case when you just want to display the file from web I would recommend to set a download url as metaData via a cloud function and the google cloud admin api.
the cloud fuction would look like this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {Storage} = require('#google-cloud/storage');
const gcs = new Storage({keyFilename: 'serviceAccountKey.json'})
exports.profilePicMetaDataChanged = functions.storage.object().onFinalize( object => {
const fileBucket = object.bucket; // The Storage bucket that contains the file.
const filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
let filePathElements = filePath.split("/");
//Here you should make sure the only files that have the right path are edited
const fileName = filePathElements.pop();
const fileDir = filePathElements.join("/") + "/";
const bucket = gcs.bucket(fileBucket);
const file = bucket.file(filePath);
file.getSignedUrl({
action: 'read',
expires: '03-09-2491'
}).then( signedUrls => {
let picDownloadUrl = signedUrls [0];
file.setMetaData({
contentType: 'image/jpeg', // or 'image/png' if file is png
metadata: {
downLoadUrl: picDownloadUrl;
}
})
});
return true;
});
After doing this you can do this on the clientside to get the download url
// Create reference to the file whose metadata we want to retrieve
let forestRef = storageRef.child("images/forest.jpg")
// Get metadata properties
forestRef.getMetadata { metadata, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Metadata now contains the metadata for 'images/forest.jpg'
}
}
The swift code was taken from here: https://firebase.google.com/docs/storage/ios/file-metadata

Get pdf from Firebase Storage

Let's say I have a PDF file with 10 pages in Firebase Storage.
In the Firebase Storage references it has the code below.
let islandRef = storageRef.child("images/island.jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
islandRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
}
}
I have tried this and did my best to download and also show the pdf file using PDFKit. But I always get an error from Firebase or something wrong in the console.
The way I did it was change the file name to the correct one and also change the number "1" to "10"
I don't know how to use the local files like in the reference.
//edited
let storage = Storage.storage()
let storageRef = storage.reference(forURL: "gs://---.appspot.com")
let dataRef = storageRef.child("Slide.pdf")
let downloadTask = dataRef.getData(maxSize: 100 * 2000 * 2000) { data, error in
if (error != nil) {
print("Uh-oh, an error occurred!")
} else {
print("download success!!")
let pdf:PDFDocument = PDFDocument(data: data!)!
self.userDefaults.setValue(pdf, forKey: "PDF-Slide")
}
}
EDIT
Sorry for asking such a crazy question.
I got it fixed, I had the pdf file downloaded all along, and I was trying to save a pdf file to userdefault so the app always crashed since it doesn't support saving pdfdocuments
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String userEmail = user.getEmail();
StorageReference str = FirebaseStorage.getInstance().getReference().child(userEmail+".pdf");
Toast.makeText(this, "pdf"+str, Toast.LENGTH_SHORT).show();

Upload Image to Amazon S3 Using Swift3

After looking around at different threads such as these (Swift - AWS S3 Upload Image from Photo Library and download it), and (Upload image AWS S3 bucket in swift), I got pretty close to getting an image upload to work but can't figure out what I'm doing wrong?
I get the following error below in my console.
Error: Error Domain=com.amazonaws.AWSS3TransferUtilityErrorDomain Code=1 "(null)" UserInfo={Server=AmazonS3, Transfer-Encoding=Identity, Connection=close, Content-Type=application/xml, Date=Tue, 13 Dec 2016 05:58:32 GMT, x-amz-request-id=2A76DF0FE33476C5, x-amz-id-2=QWZgOETbWQfddlqKmm0w3Z9HFGM2x1DWnrFjukiajTsIXfbSt9W0orTkoZeNXH/bI1xfc3mxI4Q=, x-amz-bucket-region=us-west-1}
My code is below:
let myIdentityPoolId = "us-west-2:dca2beb4-etcetcetc...."
let credentialsProvider:AWSCognitoCredentialsProvider = AWSCognitoCredentialsProvider(regionType: AWSRegionType.usWest2, identityPoolId: myIdentityPoolId)
let configuration = AWSServiceConfiguration(region: AWSRegionType.usWest2, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
I then call to upload my image with a function I made below
func uploadImage(filename:String){
print("AWS Upload Image Attempt...")
//defining bucket and upload file name
let S3BucketName: String = "distribution-tech-mobile"
let filepath = "\(AppDelegate.appDelegate.applicationDocumentsDirectory())/\(filename)"
let imageURL = URL(fileURLWithPath: filepath)
let S3UploadKeyName = filename //TODO: Change this later
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = S3BucketName
uploadRequest?.key = filename
uploadRequest?.contentType = "image/jpeg"
uploadRequest?.body = imageURL
uploadRequest?.serverSideEncryption = AWSS3ServerSideEncryption.awsKms
uploadRequest?.uploadProgress = { (bytesSent, totalBytesSent, totalBytesExpectedToSend) -> Void in
DispatchQueue.main.async(execute: {
self.amountUploaded = totalBytesSent // To show the updating data status in label.
self.fileSize = totalBytesExpectedToSend
print("\(totalBytesSent)/\(totalBytesExpectedToSend)")
})
}
self.uploadCompletionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)");
}
else{
print("Sucess")
}
})
}
let transferUtility = AWSS3TransferUtility.default()
let expression = AWSS3TransferUtilityUploadExpression()
transferUtility.uploadFile(imageURL, bucket: S3BucketName, key: S3UploadKeyName, contentType: "image/jpeg", expression: expression, completionHander: uploadCompletionHandler).continue({ (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let exception = task.exception {
print("Exception: \(exception.description)")
}
if let _ = task.result {
print("Upload Starting!")
}
return nil;
})
}
I get the console message "Upload Starting" and then a message "Failed with error" (which comes from my completion handler), followed by the error I assume from Amazon.
Any thoughts on what I'm doing wrong?
Okay I found the answer, but I have a different problem now that I'll post in another question regarding showing upload progress.
The answer was my bucket was created in the incorrect region. I created my credentials in Oregon, which is Us-West-2, and I created the bucket in Northern California by accident the first time. This apparently created the error.

Swift Firebase Storage How to retrieve image with unknow name(NSUUID)

I am making a function to retrieve the url as the user Image. However, my upload image name function created by NSUUID. Therefore, I would not know what is the name of each user profile picture. How could I improve my code to get the user imgae for every user instead of hard coding the img name?
func getUserProfilePic(){
let uid = FIRAuth.auth()?.currentUser?.uid
let profilePath = refStorage.child("\(uid)").child("profilePic/xxxxx.jpg") // xxxxx = NSUUID
profilePath.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("got no pic")
} else {
let profilePic: UIImage! = UIImage(data: data!)
self.imageV.image = profilePic
print("got pic")
}
}
}
The path is uid/profilePic/--<-file name->--
upload function
func uploadingPhoto(){
let uid = FIRAuth.auth()?.currentUser?.uid
let imgName = NSUUID().UUIDString + ".jpg"
let filePath = refStorage.child("\(uid!)/profilePic/\(imgName)")
var imageData = NSData()
imageData = UIImageJPEGRepresentation(profilePic.image!, 0.8)!
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpg"
let uploadTask = filePath.putData(imageData, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
}else{
let downloadURL = metaData!.downloadURL()!.absoluteString
let uid = FIRAuth.auth()?.currentUser?.uid
let userRef = self.dataRef.child("user").child(uid!)
userRef.updateChildValues(["photoUrl": downloadURL])
print("alter the new profile pic and upload success")
}
}
I highly recommend using Firebase Storage and the Firebase Realtime Database together to store that UUID -> URL mapping, and to "list" files. The Realtime Database will handle offline use cases like Core Data as well, so there's really no reason to futz with Core Data or NSUserDefaults. Some code to show how these pieces interact is below:
Shared:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
Upload:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
Download:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Do something with downloaded data...
})
})
For more information, see Zero to App: Develop with Firebase, and it's associated source code, for a practical example of how to do this.
There can be two ways to go about this :-
1.) Store the Firebase Storage path of users profile_picture in your Firebase database and retrieve every time before you start downloading your profile picture.
2.) Store the file path in CoreData every time your user uploads a profile picture and hit that path every time to get file_Path to where you stored that user's profile_pic .
Storing the path :-
func uploadSuccess(metadata : FIRStorageMetadata , storagePath : String)
{
print("upload succeded!")
print(storagePath)
NSUserDefaults.standardUserDefaults().setObject(storagePath, forKey: "storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)")
//Setting storagePath : (file path of your profile pic in firebase) to a unique key for every user "storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)"
NSUserDefaults.standardUserDefaults().synchronize()
}
Retrieving your path, Every time you start downloading your image :-
let storagePathForProfilePic = NSUserDefaults.standardUserDefaults().objectForKey("storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)") as? String
Note :- i am using currentUser ID, you can use USER SPECIFIC id ,if you want to download multiple users profile pics, all you need to do is put their uid in place.