Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing S3 file url after successful upload #36

Closed
btoone opened this issue Aug 13, 2014 · 6 comments
Closed

Accessing S3 file url after successful upload #36

btoone opened this issue Aug 13, 2014 · 6 comments

Comments

@btoone
Copy link

btoone commented Aug 13, 2014

Currently the only way I can figure out how to access the S3 file url is to have a $watch on the model in my controller.

It seems like a better solution would be to have the the event s3upload:success include the file url as an additional argument. Then I could simple add a listener in my controller and do whatever I wanted with the file url.

Is there already a better way to access the file url from the controller? If not is adding it to the events a good option?

@btoone btoone changed the title Accessing key (filename on S3) after successful upload Accessing S3 file url after successful upload Aug 15, 2014
@juliussss
Copy link

hi @caspyin are you able to share a gist of how you accessed the s3 file url using a $watch? I can't seem to pass a variable assigned with the url value from within a $watch function (only propogates a string I believe)...quite frustrating as this module is great in what it does but becomes rather redundant without a way to access the file in the server response callback.

@onorok
Copy link

onorok commented Sep 26, 2014

@juliussss Can you show the code you have now and maybe I can help you out?

I have mine being returned to a promise after the upload takes place and then you have all the data returned for the current upload. The trick is parsing it from XML to JSON so you can store it in a scope variable. Something like this:

scope.upload[i].then(function(response) {
     if (response.status === 201) {
          $scope.fileUploading = false;
          var data = xml2json.parser(response.data);
          var parsedData = {
               location: data.postresponse.location,
               bucket: data.postresponse.bucket,
               key: data.postresponse.key,
               etag: data.postresponse.etag
          };
          messageService.newMessage('Your photo uploaded successfully.','success',5);
          $scope.imageUploads.push(parsedData);
          $scope.saveS3Photo($scope.imageUploads);
          $scope.nextPhoto();
     } else {
          messageService.newMessage('There was an error uploading your image.  Please try again later.','error',5);
     }

As you can see, I'm taking the response from the upload, parsing it, then pushing that parseData directly into the $scope.imageUploads variable. It instantly becomes available to use inside your HTML at that point. Mine is for an image so I show the user their image they uploaded with a success message.

@juliussss
Copy link

Hey @onorok thanks for the reply...

To illustrate my setup — I have a userprofile.html view which contains a form with multiple fields (name, email, avatar, etc) that is populated from properties of the current logged in user. I am using the ng-s3Upload module for the avatar image upload part of the form. Here's a snippet:

<form name="user_settings" ng-controller="EditUserCtrl">
 <div>
  <label for="fullname">Full Name</label>
  <input name="fullname" type="text" ng-model="user.full_name" value="{{ user.full_name }}">
 </div>
 <div>
  <label for="photo">Photo (s3 location: {{ userToUpdate.avatar_location }})</label>
  <div s3-upload bucket="'s3BucketName'" ng-model="user.avatar_location" s3-upload-options="{getOptionsUri: '/upload', folder: 'uploads/', enableValidation: 'false'}"></div>
 </div>
   <!-- // more form fields -->
 <div>
  <input type="submit" value="Submit" ng-click="updateUser()" />
 </div>
</form>

This view uses a controller named EditUserCtrl which is obviously different to the controller defined in the ng-s3Upload module / directive. The following is a snippet from this EditUserCtrl controller which attempts to capture the uploaded s3 file URL, assign it to a variable and then update the user property accordingly before sending back to the server.

// when a new file is uploaded assign the new s3 url to the $scope.s3Url variable
$scope.$watch('user.avatar_location', function(newVal, oldVal) {
  $scope.s3Url = newVal;
  console.log(oldVal, newVal);
});     

// submit button function to update the user
$scope.updateUser = function() {
  // use the URL location that was assigned in the $watch function
  $scope.user.avatar_location = $scope.s3Url; 
  // save the user settings using the service
  usersService.update({id: $scope.user.id}, $scope.user, function() {
   // redirect on success
   $window.location.href = '/users/' + $scope.currentUser.id;
  }, function(error) {
   // log error
  });
};

The reason for trying to tackle the problem this way is because the ng-s3Upload directive uploads the file instantly (i.e. it does not seem to have an option to upload the file only when clicking the form's submit button, which then means I do not have an s3 server response to extract the URL from as would be preferred), so I try to capture the s3 file URL by implementing a $watch function but sadly this also fails.

Hopefully this makes sense. Happy to give more clarity if needed and thanks again for trying to help!

@btoone
Copy link
Author

btoone commented Sep 26, 2014

@juliussss I think I had the same problem with the $watch fn. It has been a while but it seems like there was a timing issue.

Anyway I ended up adding to the uploadComplete() fn to emit the file url on success. Then I just use an event listener in my controller to do what I want with the file url.

// ng-s3upload.js (local hack)
function uploadComplete(e) {
  var xhr = e.srcElement || e.target;
  scope.$apply(function () {
    self.uploads--;
    scope.uploading = false;
    if (xhr.status === 204) { // successful upload
      scope.success = true;
      deferred.resolve(xhr);

      // Modified to include the file url
      scope.$emit('s3upload:success', xhr, uri + key);

    } else {
      scope.success = false;
      deferred.reject(xhr);
      scope.$emit('s3upload:error', xhr);
    }
  });
}
// controller.js
$scope.$on('s3upload:success', function (evt, xhr, fileUrl) {

  // do stuff here with fileUrl
})

$scope.$on('s3upload:error', function (evt, xhr) {
  // alert the failure
})

asafdav added a commit that referenced this issue Sep 28, 2014
@asafdav
Copy link
Owner

asafdav commented Sep 28, 2014

Hi all, sorry for my late response, my son was born few weeks ago and it's been hectic lately.

@caspyin I liked you solution, and implemented it as part as the library. Thanks a lot !

@asafdav asafdav closed this as completed Sep 28, 2014
@juliussss
Copy link

Thanks @caspyin, works wonderfully 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants