I'm writing a program that uploads images to Azure blob storage and stores them in folders. The images are stored in an image array before they're uploaded.
Now I want to be able to retrieve a folder of images and store it back in an image array.
Do I need to create a blobContainer locally with the folder name I'm looking to download and then download it?
Or can I only download the images one by one.
//Creating the Container
let blockBlob = blobContainer.blockBlobReference(fromName: "folderName")
blockBlob.properties.contentType = "image/png"
//Download the container
blockBlob.download(to: imageArray, completionHandler: {(NSError) -> Void in })
Is this the correct idea for how this should be done?
For now, we can't download the whole container/folder using one single API/SDK operation according to Azure Blob REST API.
So your second assumption is right--download your images to local stream/file one by one in a loop.
You can add one more DownloadBlob step in ListBlobs process.
Update
Get your folder aka the directory using method directoryReferenceFromName in container.
Then use listBlobsSegmentedWithContinuationToken to list and download blobs in directory. And the count method is also available.
Related
I would like to create a function that gets the data in my firebase cloud storage. For example, I have 3 separate folders as follows: Movies, Songs and previews. I would like to create a function that will be able to download the url file of the specified folder to get the data of it so I can display it on the selection screen. I have the folders pretty organized. They're in this order, Movies/MovieName/{Movie.jpg (image for the movie), Movie.mp4(video)}. I need my function to open up "Movies" and run through the MovieName file and post each of the contents inside those files. So kinda like a streaming service, I do NOT want to download the url file permanently on the localfile. So kinda think of netflix where they have the image of the movie, with a little description, reviews, etc and it'll stream. You can never download the movie permanently.
"What have I tried?":
I've tried using the firebase link here to guide me but it seems like I'm not understanding it. I've also tried the "list all" list all link but I don't believe it shows the data of each file. Nor was it working for me. Finally I've tried using the URLSession.streamtask()
but I've never used that before and so I'll try to successfully use it now by researching more.
"Some code":
func getAlbums() {
storageref.downloadURL { (url, error) in
if let error = error {
print(error.localizedDescription)
}
else {
//get download url
}
}
According to the firebase link attached this is what I need to do but I don't know how to get the download URL after the else statement.
I'm using the AWS iOS SDK and have been able to do the upload / download file operations as outlined in the tutorials. Now I'm trying to copy a file from one bucket and paste it into another. I own both buckets and have access to them. I also want to delete the file from the first bucket after copying it (so technically this is a cut-paste operation) but I'm assuming the way to do that is copy, paste, delete the original.
After some digging it seems like the way to do this is through the AWSS3 -uploadPartCopy: function. It seems like this function uses an AWSS3UploadPartCopyRquest object which has 3 relevant input properties, the destination bucket (bucket), the destination key (key) and the source location (replicateSource), which seems to be a URL for the location of the object to be copied.
This seems to me like a really strange format for such a function, and I'm also not familiar with what Uploading a part means, i.e. does this have to be part of a multi-part upload? Do I need to start a multi-part upload before calling uploadPartCopy?
I'm also not sure this is the way to go about this. It seems like an overcomplicated solution to a relatively simple task. Am I on the right track here?
Just Refer below code. It gives you idea in detail for copy data from one bucket to another. In my case i want to copy multiple images from same bucket.
NSString *sourceBucket = #"treedev1234";
NSString *destinationBucket = #"treedev1234";
AWSS3 *s3 = [AWSS3 defaultS3];
AWSS3ReplicateObjectRequest *replicateRequest = [AWSS3ReplicateObjectRequest new];
for(int i = 0;i<feedModel.imageCount;i++){
replicateRequest.bucket = destinationBucket;
replicateRequest.key = [NSString stringWithFormat:#"posts/%d/%d.jpg",newpostid,i];
replicateRequest.replicateSource = [NSString stringWithFormat:#"%#/posts/%d/%d.jpg",sourceBucket,oldpostid,i];
replicateRequest.ACL = AWSS3ObjectCannedACLPublicReadWrite;
[[s3 replicateObject:replicateRequest] continueWithBlock:^id(AWSTask *task) {
if(task.error)
NSLog(#"The share request failed. error: [%#]", task.error);
return nil;
}];
There are two operations to copy an object: PUT Object - Copy and Upload Part - Copy. If the object is not too large, "PUT Object - Copy", which is mapped to - replicateObject:, is easier to implement.
Also, Amazon S3 has the Cross-Region Replication feature, which automatically replicates objects if your two buckets are not in the same region.
I am loading an array of UIImage from the iOS Documents directory:
var images = [UIImage]()
for fileName in fileNames {
images.append(UIImage(contentsOfFile: "\(imagesPath)/\(fileName).png")!)
}
I'm going to continue using this array but I don't need the files anymore, so I go ahead and delete them:
for fileName in fileNames {
do {
try NSFileManager.defaultManager().removeItemAtPath("\(imagesPath)/\(fileName).png")
} catch {
print("Error")
}
}
When I do this, my array of UIImage is now invalid and gives me errors while trying to access them. Shouldn't this be in memory and not related to the files on disk?
I tried using the ".copy()" command on the images when I load them but that made no difference.
I have confirmed that the delete is the issue above because if I comment out that line the app works great with no errors. I only get errors accessing the array after I delete the files from disk.
Is there a way to sever this connection?
Edit: Per the correct answer from #Wain, the code works fine if I change it to:
var images = [UIImage]()
for fileName in fileNames {
let imgData = NSFileManager.defaultManager().contentsAtPath("\(imagesPath)/\(fileName).png")!
images.append(UIImage(data: imgData)!)
}
Doing this doesn't keep the link back to the file on disk.
The images haven't been displayed so the data hasn't fully been loaded yet, only enough to know the image details and size has been loaded. This is for memory efficiency. depending on the image format and usage different parts of data may be loaded at multiple different times.
If you load the file into NSData, ensuring that it isn't memory mapped, and create the image from that then the data and image should be unlinked from the underlying file. This is less memory efficient and it would be better to keep your current code and delete the files when you know you're finished with the images all together.
I use AlamofireImage in my project. I use someImageView.af_setImageWithURL(url) a lot.
However at some point I need to fetch an image manually from the imageCache, because I do not want to apply it to an UIImageView.
This brings up 2 problems:
I can access the image cache directly by creating one with let imageCache = AutoPurgingImageCache(). Is this the same cache as .af_setImageWithURL() uses (Singleton)? Or is this a new one? Which is worthless in my case, because I could not profit from the prefilled cache.
Given this is the same cache: How can I fetch the correct image? I thought about imageCache.imageForRequest(NSUrlRequest). Will this match on the images I downloaded before with .af_setImageWithURL() if the NSUrlRequest does use the same NSURL?
Question 1
That is not the same cache that UIImageView uses. If you need access the same cache, you can do so by UIImageView.af_sharedImageDownloader.imageCache.
Question 2
You will need to use the exact same NSURLRequest and the same ImageFilter. If you use the same NSURLRequest and the same ImageFilter, the image will be directly fetched from the cache if it exists. If you are using a different ImageFilter, then the original image will most likely be pulled from the NSURLCache, then the ImageFilter will be run over it, placed into the AutoPurgingImageCache and returned. If the NSURLRequest is different, the new image will need to be downloaded again.
This is answer to the comment: How to download multiple images, save them to cache and get from cache.
Following code will download images from array of URLs and cache them in-memory.
let imageDownloader = ImageDownloader()
var URLRequests = [URLRequestConvertible]()
URLRequests.append(NSURLRequest(URL: NSURL(string: "https://some/image/some.png")!))
As #cnoon mentioned, to set an image use myUIImageView.af_setImageWithURL(imageUrl)
This will load image from cache if present. If it doesn't exist, then network call will happen
I want to allow my app to download images from the web to be placed in /images and then displayed in the UI - this is to allow branding specific images to be downloaded for a multitenant app.
The app downloads the image fine and places the image into the /Images folder
However when trying to load the image using UIImage.FromFile it always returns null - even though I know full well the image is there!
I know UIImage.FromBundle caches stuff so I chose to use FromFile but this doesn't seem to work?!
Is there something I need to do to make this work? Or am I pushing my luck a little?
I am using the following to create the file path:
public static UIImage SafeClaim_Logo = UIImage.FromFile(NSBundle.MainBundle.BundlePath +"/Images/Logo.png");
The following is the code I use to download and save the file - the file path from the above is the same below when I inspect it
HttpClient client = new HttpClient ();
var clientResponse = await client.GetByteArrayAsync(url);
using (FileStream fs = new FileStream(NSBundle.MainBundle.BundlePath + path, FileMode.OpenOrCreate))
{
var bytes = clientResponse;
fs.Write(bytes, 0, bytes.Length);
}
The app bundle is read-only so you can't change or add something in there at runtime.
All content created at runtime should be stored in your documents or cache folders.
You can get the documents folder by calling:
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
One good place to store this kind of files is the Library/ folder. On iOS 7 you can access it using:
var libraryFolderPath = Path.GetFullPath(
Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments), "..", "Library"));
On iOS 8:
var url = NSFileManager.DefaultManager.GetUrls (
NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomain.User) [0];
var libraryFolderPath = url.Path;
The reason is explained by Xamarin documentation:
The Library directory is a good place to store files that are not
created directly by the user, such as databases or other
application-generated files. The contents of this directory are never
exposed to the user via iTunes.
You can create your own subdirectories in Library; however, there are
already some system-created directories here that you should be aware
of, including Preferences and Caches.
The contents of this directory (except for the Caches subdirectory)
are backed up by iTunes. Custom directories that you create in Library
will be backed up.
If you need to enable iTunes file sharing and expose the content of the Documents/ folder, your files are not visible to the user.