Firebase Storage Async Image Download not in the right order - arrays

I am met with a problem when I tried to download a batch of images off firebase storage. Basically, because the file sizes differ, the images are not appended to the image array properly causing the images to be in the wrong order that I wanted. Below is the code
import Foundation
import FirebaseStorage
class GalleryCellDetailedData {
var selectedGallery:String?
var count:Int
init(selectedGallery:String?,count:Int){
self.selectedGallery = selectedGallery
self.count = count
}
func addImages(completion:(data:[NSData])->()){
var datas = [NSData]()
let myGroup = dispatch_group_create()
for i in 0..<count {
dispatch_group_enter(myGroup)
getImage(i, completion: { (image:NSData) in
datas.append(image)
print("Finish Request \(i)")
dispatch_group_leave(myGroup)
})
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
completion(data: datas)
})
}
private func getImage(number:Int, completion:(image:NSData)->()){
let storage = FIRStorage.storage()
//Reference to Firebase Profile Picture Storage
let storageRef = storage.referenceForURL("gs://mannacatering-addcb.appspot.com")
print("Initiating Image Download")
let galleryPicRef = storageRef.child("Gallery/\(selectedGallery!)/g\(String(number)).jpg")
//Download Image
galleryPicRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("fail to download image")
}
dispatch_async(dispatch_get_main_queue(), {
print("Dispatching image")
completion(image:data!)
})
}
}
}
I split them up into 2 separate functions because I tried to manage them in a single function and the order is in a mess as well and I thought this might work but apparently not.

Instead of storing your data in an array, store it in a dictionary. The key can be the number i or however you want to refer to an image when you use it.

I recommend taking an approach similar to Storing multiple images into firebase and getting urls, where you store the URLs in the Realtime Database, and use that as your source of truth for ordering, display, etc. It's far easier and makes for better apps :)

Related

Share file from url

I'm working on a project that needs to share a pdf file and that file is stored in the firebase storage.
I've researched different sharing libs and all seems to share files from the internal storage or share the url directly as text (this could be an option, but product team doesn't want it).
So my question is, is there a way different than manually download the pdf, save it in the internal storage and then share it as any file?
This seems to me like a not so strange use case, so someone may have a better solution :)
You can use this package to share files https://pub.dev/packages/esys_flutter_share
Future<void> _shareMixed() async {
try {
final ByteData bytes1 = await rootBundle.load('assets/image1.png');
final ByteData bytes2 = await rootBundle.load('assets/image2.png');
final ByteData bytes3 = await rootBundle.load('assets/addresses.csv');
await Share.files(
'esys images',
{
'esys.png': bytes1.buffer.asUint8List(),
'bluedan.png': bytes2.buffer.asUint8List(),
'addresses.csv': bytes3.buffer.asUint8List(),
},
'*/*',
text: 'My optional text.');
} catch (e) {
print('error: $e');
}
}
full example can be found in following link code

How can I check if current user is part of a sharepoint group with PnPJS?

I need to display content based on the group the current user belongs to but can't figure out how to do this in react/pnpjs.
I wrote the function below but it returns false even when the group name returned in console.log(grp["Title"]) is correct.
private _checkUserInGroup(strGroup)
{
let InGroup:boolean = false;
let grp = sp.web.currentUser.groups.get().then((r: any) => {
r.forEach((grp: SiteGroups) =>{
if (grp["Title"] == strGroup)
{
InGroup = true;
}
console.log(grp["Title"]);
});
});
return InGroup;
}
Your request is pretty wide because we don't really know what you have tried.
Step wise
Figure out what groups the current user is a member of.
Fetch appropriate data in to some form of memory store.
Display the data from memory store.
Which step is it that you are struggling with?
https://pnp.github.io/pnpjs/sp/site-users/
import { sp } from "#pnp/sp";
import "#pnp/sp/webs";
import "#pnp/sp/site-users/web";
let groups = await sp.web.currentUser.groups();
Cheers
Truez
I managed to make this work by placing async/await in the function...it was returning false before the call to sp.web....
Thanks a lot for the help.

React native fetch... Explain it like I'm 5

export default class App extends Component {
state = {
data: []
};
fetchData = async () => {
const response = await fetch("https://randomuser.me/api?results=5"); // Replace this with the API call to the JSON results of what you need for your app.
const json = await response.json();
this.setState({ data: json.results }); // for the randomuser json result, the format says the data is inside results section of the json.
};
So, I have this code in my App.js file for React Native. The randomuser.me is a website that just gives you random users. Using it as a test URL right now. I don't really understand what the code is doing enough to be able to use it for other parts of my project. I was able to successfully display the 5 user results but now I want to access them again and iterate through the data attribute of the state.
tldr; Can I just access the data I got from the fetch in a for loop using data[i]? Please advise. I want to see if user input matches any of the items in the response that is stored in data attribute of state.
Ok the thign that you just did, that is fetch. You retrieve data from the internet.
"https://randomuser.me/api?results=5" is an API, there is lot of different API's, and each one has it´s own way to retrieve data from. if you put "https://randomuser.me/api?results=5" in your browser, you are gonna see a JSON, some API's store data in JSON, others in an array format.
In this case, the JSON, has just one child, 'results', thats why you store "json.results".
That´s fetch. The thing that you want to do is just javascript.
Store json.results in a variable
then iterate over it
var Results = json.results //store it in a variable
for(var i = 0;i<Object.keys(Results).length;i++){ //iterate
var CurrentUser = Results[Object.keys(Results)[i]] // i use this because some JSOn have random keys
if(CurrentUser.gender==='male'){//if you meet a condition
//do whatever you want
}
}
you can also use ".map" if it´s an array

Ways to access firebase storage (photos) via web app

I'm confused as to the appropriate way to access a bunch of images stored in Firebase storage with a react redux firebase web app. In short, I'd love to get a walkthrough of, once a photo has been uploaded to firebase storage, how you'd go about linking it to a firebase db (like what exactly from the snapshot returned you'd store), then access it (if it's not just <img src={data.downloadURL} />), and also how you'd handle (if necessary) updating that link when the photo gets overwritten. If you can answer that, feel free to skip the rest of this...
Two options I came across are either
store the full URL in my firebase DB, or
store something less, like the path within the bucket, then call downloadURL() for every photo... which seems like a lot of unnecessary traffic, no?
My db structure at the moment is like so:
{
<someProjectId>: {
imgs: {
<someAutoGenId>: {
"name":"photo1.jpg",
"url":"https://<bucket, path, etc>token=<token>"
},
...
},
<otherProjectDetails>: "",
...
},
...
}
Going forward with that structure and the first idea listed, I ran into trouble when a photo was overwritten, so I would need to go through the list of images and remove the db record that matches the name (or find it and update its URL). I could do this (at most, there would be two refs with the old token that I would need to replace), but then I saw people doing it via option 2, though not necessarily with my exact situation.
The last thing I did see a few times, were similar questions with generic responses pointing to Cloud Functions, which I will look into right after posting, but I wasn't sure if that was overcomplicating things in my case, so I figured it couldn't hurt too much to ask. I initially saw/read about Cloud Functions and the fact that Firebase's db is "live," but wasn't sure if that played well in a React/Redux environment. Regardless, I'd appreciate any insight, and thank you.
In researching Cloud Functions, I realized that the use of Cloud Functions wasn't an entirely separate option, but rather a way to accomplish the first option I listed above (and probably the second as well). I really tried to make this clear, but I'm pretty confident I failed... so my apologies. Here's my (2-Part) working solution to syncing references in Firebase DB to Firebase Storage urls (in a React Redux Web App, though I think Part One should be applicable regardless):
PART ONE
Follow along here https://firebase.google.com/docs/functions/get-started to get cloud functions enabled.
The part of my database with the info I was storing relating to the images was at /projects/detail/{projectKey}/imgs and had this structure:
{
<autoGenKey1>: {
name: 'image1.jpg',
url: <longURLWithToken>
},
<moreAutoGenKeys>: {
...
}, ...}
My cloud function looked like this:
exports.updateURLToken = functions.database.ref(`/projects/detail/{projectKey}/imgs`)
.onWrite(event => {
const projectKey = event.params.projectKey
const newObjectSet = event.data.val()
const newKeys = Object.keys(newObjectSet)
const oldObjectSet = event.data.previous.val()
const oldKeys = Object.keys(oldObjectSet)
let newObjectKey = null
// If something was removed, none of this is necessary - return
if (oldKeys.length > newKeys.length) {
return null
}
for (let i = 0; i < newKeys.length; ++i) {// Looking for the new object -> will be missing in oldObjectSet
const key = newKeys[i]
if (oldKeys.indexOf(key) === -1) {// Found new object
newObjectKey = key
break
}
}
if (newObjectKey !== null) {// Checking if new object overwrote an existing object (same name)
const newObject = newObjectSet[newObjectKey]
let duplicateKey = null
for (let i = 0; i < oldKeys.length; ++i) {
const oldObject = oldObjectSet[oldKeys[i]]
if (newObject.name === oldObject.name) {// Duplicate found
duplicateKey = oldKeys[i]
break
}
}
if (duplicateKey !== null) {// Remove duplicate
return event.data.ref.child(duplicateKey).remove((error) => error ? 'Error removing duplicate project detail image' : true)
}
}
return null
})
After loading this function, it would run every time anything changed at that location (projects/detail/{projectKey}/imgs). So I uploaded the images, added a new object to my db with the name and url, then this would find the new object that was created, and if it had a duplicate name, that old object with the same name was removed from the db.
PART TWO
So now my database had the correct info, but unless I refreshed the page after every time images were uploaded, adding the new object to my database resulted (locally) in me having all the duplicate refs still, and this is where the realtime database came in to play.
Inside my container, I have:
function mapDispatchToProps (dispatch) {
syncProjectDetailImages(dispatch) // the relavant line -> imported from api.js
return bindActionCreators({
...projectsContentActionCreators,
...themeActionCreators,
...userActionCreators,
}, dispatch)
}
Then my api.js holds that syncProjectDetailImages function:
const SAVING_PROJECT_SUCCESS = 'SAVING_PROJECT_SUCCESS'
export function syncProjectDetailImages (dispatch) {
ref.child(`projects/detail`).on('child_changed', (snapshot) => {
dispatch(projectDetailImagesUpdated(snapshot.key, snapshot.val()))
})
}
function projectDetailImagesUpdated (key, updatedProject) {
return {
type: SAVING_PROJECT_SUCCESS,
group: 'detail',
key,
updatedProject
}
}
And finally, dispatch is figured out in my modules folder (I used the same function I would when saving any part of an updated project with redux - no new code was necessary)

How to save image from swift to SQL Server database using webservice?

I converted ipad signature to png image successfully using UIImagePNGRrepresentation(Image). Now I want to store this Image from swift to a SQL Server database using a web service. I have not any idea about how do this?
This is my swift code
UIGraphicsBeginImageContextWithOptions(self.signatureMainImageview.bounds.size, false, 0.0)
self.signatureMainImageview.image?.drawInRect(CGRectMake(0, 0, self.signatureMainImageview.frame.size.width, self.signatureMainImageview.frame.size.height))
let SaveImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let image = UIImagePNGRepresentation(SaveImage)
var CardDataObj = structCardData()
CardDataObj.CustomerSignature = image!
let requestCardData = NSMutableURLRequest(URL: NSURL(string: "http://URL")!)
requestCardData.HTTPMethod = "POST"
let postString = CardDataObj.jsonRepresentation
requestCardData.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(requestCardData) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
Now I want to know how to get this image in webservice? which datatype use in webservice for image? which datatype use in sql for image? How to send this image to sql?
Rather than a data task you need an upload task. Either uploadTaskWithRequest:fromData:completionHandler or its file or stream variants
In order to begin the task you need to call task.resume()
It also helps to retrieve the response if you cast to HTTPURLResponse like so:
if let response = response as? NSHTTPURLResponse {
response.statusCode
response.allHeaderFields
}
I wrote a blogpost on uploading using a stream, which might be of some use. Here's also a more general post about NSURLSession.
The first blogpost linked to will give you some server-side code in PHP to receive a stream, but if you are uncertain about what to do on the SQL I'd recommended breaking this question into two and asking that question separately.

Resources