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

allow upload of blob #208

Closed
bettysteger opened this issue Aug 12, 2014 · 83 comments
Closed

allow upload of blob #208

bettysteger opened this issue Aug 12, 2014 · 83 comments

Comments

@bettysteger
Copy link

hey I just used https://github.com/alexk111/ngImgCrop to crop an image before uploading.. and now I did this to upload the blob with angular-file-upload:

  uploader.onBeforeUploadItem = function(item) {
    var blob = dataURItoBlob($scope.croppedImage);
    item._file = blob;
  };

This works just fine, but the file name is ignored because of this line: (and because of that the server doesn't recognize the file extension)

form.append(item.alias, item._file);
-->
------WebKitFormBoundaryGD5fYyZwXHVXxfZ4
Content-Disposition: form-data; name="avatar"; filename="blob"
Content-Type: image/png

It would be possible to add the name as third parameter, see https://developer.mozilla.org/en-US/docs/Web/API/FormData

what do you think of adding a third parameter or allow to upload a Blob in the first place?

btw: you made a really cool plugin!

@nervgh
Copy link
Owner

nervgh commented Aug 17, 2014

Hi. Thanks.

allow upload of blob

Blob uploading is possible. You can pass any data with file. Probably, in your case it looks like this:

// client
uploader.onAfterAddingFile = function(item) {
    item.formData.push({name: item.file.name});
};
// server
$name = $_POST['name'];

what do you think of adding a third parameter or allow to upload a Blob in the first place?

I can write something like this

form.append(item.alias, item._file, item.file.name);

insted that code. What do you think about it?

@bettysteger
Copy link
Author

Yes @nervgh, changing this line of code is what I meant ;)
because then I don't need to add additional formData (client) and I don't need to get the name extra on the server, because it would work out of the box (with carrierwave)

@bettysteger
Copy link
Author

Blob uploading is possible. You can pass any data with file.

yes, I know that, but I don't want it to pass it with the file, I want to just upload a Blob... that's why I overwrite item._file before the upload ;)

@nervgh
Copy link
Owner

nervgh commented Aug 18, 2014

Thanks =)

@nervgh nervgh closed this as completed Aug 18, 2014
@EnchanterIO
Copy link

@lpsBetty @nervgh Do any of you have a functional example of using ngImgCrop together with this uploader? I just can't make it work.

@bettysteger
Copy link
Author

bettysteger commented Sep 23, 2014

hey @TrkiSF2, I used onBeforeUploadItem to overwrite the actual '_file' with the blob:

updated solution, if you have a scope inheritance problem: https://gist.github.com/lpsBetty/2057e0bd0b4142d4070db0e1175ca47e

  /**
   * Show preview with cropping
   */
  uploader.onAfterAddingFile = function(item) {
    $scope.croppedImage = '';
    var reader = new FileReader();
    reader.onload = function(event) {
      $scope.$apply(function(){
        $scope.image = event.target.result;
      });
    };
    reader.readAsDataURL(item._file);
  };

  /**
   * Upload Blob (cropped image) instead of file.
   * @see
   *   https://developer.mozilla.org/en-US/docs/Web/API/FormData
   *   https://github.com/nervgh/angular-file-upload/issues/208
   */
  uploader.onBeforeUploadItem = function(item) {
    var blob = dataURItoBlob($scope.croppedImage);
    item._file = blob;
  };

  /**
   * Converts data uri to Blob. Necessary for uploading.
   * @see
   *   http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata
   * @param  {String} dataURI
   * @return {Blob}
   */
  var dataURItoBlob = function(dataURI) {
    var binary = atob(dataURI.split(',')[1]);
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
    var array = [];
    for(var i = 0; i < binary.length; i++) {
      array.push(binary.charCodeAt(i));
    }
    return new Blob([new Uint8Array(array)], {type: mimeString});
  };

and the HTML:

        <!-- crop area if uploaded image-->
        <img-crop ng-show="image" image="image" result-image="croppedImage" area-type="square" result-image-size="250"></img-crop>

I hope I can you help you with that snippets ;)

@trivago-llukac
Copy link

IpsBetty thank you for the code! I will try it when I come home but from a quick view I am missing one thing. When do you call something like $scope.uploader.addToQueue(item) and how the item object looks like in that state please?

@bettysteger
Copy link
Author

the uploader is bind to the dropzone and to the input field in my HTML.
so when a file is chosen uploader.onAfterAddingFile (see above) will be called

@EnchanterIO
Copy link

Thank you very much. I made it work finally!!! :) If you would like to stay in touch https://twitter.com/TrkiSF2

@nervgh
Copy link
Owner

nervgh commented Oct 13, 2014

@lpsBetty , thanks =)

@brianfeister
Copy link

Sorry to continue this thread that's already long - @TrkiSF2, any chance you could post your working code as a Plunkr or similar?

@lpsBetty - thank you SO much for posting your code. I'm just curious what the HTML context is for the upload directory? I realize that we're saving the base64 encoded image string to $scope.image. But the reference to that via your example (<img-crop image="image">) doesn't seem to be working. If I simply do <img ng-src="{{image}}"> then the data URI correctly renders an image, but passing $scope.image has no effect when used in the context of the <img-crop> directive from your example. Any ideas? This is such a WONDERFUL use case, would be great to include in the docs, since client side crop is an amazing complement to this directive.

Maybe someone can share what wraps the example crop code from here:

        <!-- crop area if uploaded image-->
        <img-crop ng-show="image" image="image" result-image="croppedImage" area-type="square" result-image-size="250"></img-crop>

@nervgh - I spent days recreating this functionality in raw javascript, and got it working. However, this directive is so well written and documented I'm definitely tossing that work in favor of this library. Great documentation, robust eventing, and modular / flexible - Thank you!!!

@brianfeister
Copy link

Ugh, sorry for the stupid question (though I think the full HTML template would still be great to post). Turns out I was just missing the CSS which is required for the ngImgCrop directive

.cropArea {
  background: #E4E4E4;
  overflow: hidden;
  width:500px;
  height:350px;
}

@brianfeister
Copy link

Thanks again @lpsBetty! I had to make some changes to accomodate multi-file uploads, I think your code was assuming singular. For anyone else that wants to see all the moving parts, I've created a public gist that wires it all together:

https://gist.github.com/brianfeister/56a1c6c77cd5928a1c53

@bettysteger
Copy link
Author

@brianfeister yes i just needed single file upload, thanks your gist 👍

@imchintoo
Copy link

Hi @nervgh
Is there any way to trigger upload image event? Actually i want to upload the image after inserting/updating the data?

@bettysteger
Copy link
Author

Hi @chin2, there is a property called autoUpload, see https://github.com/nervgh/angular-file-upload/wiki/Module-API

just set it to true

@imchintoo
Copy link

Thanks @lpsBetty 👍

@imchintoo
Copy link

Hi, @lpsBetty, @nervgh
Yes one thing is that i want to load a default image while rendering the FileUpload, is it possible?

@bettysteger
Copy link
Author

@chin2 So you want an image already in the queue? why do you want that?

@imchintoo
Copy link

@lpsBetty Actually while edit form, the uploaded image should be rendered. Have you got idea what is want exactly?

@bettysteger
Copy link
Author

@chin2 so you want a preview? just use the result-image like this:

 <!-- crop area if uploaded image-->
 <img-crop ng-show="image" image="image" result-image="croppedImage" area-type="square" result-image-size="150"></img-crop>
<!-- preview -->
<img alt="avatar_preview" ng-src="{{croppedImage}}" width="150" height="150" />

@jiangyanghe
Copy link

angular-file-upload how to add an parameter in url?
/* the 'sn' is i want to add it in the url/
var uploader = $scope.uploader = new FileUploader({
url:url+'/ver/1.0/members//orgs//vas/'+sn+'/photo' ,
autoUpload: false,
headers:{'Authorization':accessKey+':0a632a6beaf24928b1ccfdf7bc680317','Accept':'
/*'}
});
uploader.onSuccessItem = function(fileItem, response, status, headers) {
if(response.retCode == "SUCCESS"){
$scope.vasPhoto = response.data.path;
}else{
alert(response.msg);
}
};

@amuste
Copy link

amuste commented May 11, 2015

TH's @lpsBetty

@hfar
Copy link

hfar commented Jun 7, 2015

Hi,
@lpsBetty is it possible to upload both original image and the cropped image?

@bettysteger
Copy link
Author

@enuelmorales-snoop where is this error coming from?
maybe you can paste the whole stacktrace!

@enuelmorales-snoop
Copy link

enuelmorales-snoop commented Aug 18, 2016

Okey, mi view code is:

< img-crop image="myImage"
result-image="myCroppedImage"
area-type="rectangle"
change-on-fly="false"
result-width="myCroppedImageW"
result-height="myCroppedImageH"
result-x="myCroppedImageX"
result-y="myCroppedImageY"
original-width="myOriginalW"
original-height="myOriginalH"
original-crop-x="myOriginalX"
original-crop-y="myOriginalY"
original-crop-width="myCroppedOriginalW"
original-crop-height="myCroppedOriginalH" >
< /img-crop >

The problem is when I mouseup on the corner the console log return " Ignored attempt to cancel a touchstart event with cancelable=false, for example because scrolling is in progress and cannot be interrupted. " and
nothing happens and the corner does not move

@bettysteger
Copy link
Author

@enuelmorales-snoop I think you are posting in the wrong issue here, this is the angular-file-upload and not the img-crop repository.

@braindrained
Copy link

There is a way to get this work if autoUpload is set to true?

@bettysteger
Copy link
Author

@braindrained I don't understand, do you want to upload the image immediately before cropping?
Do you create the blob by yourself?

@braindrained
Copy link

@lpsBetty let me explain, I use angular file upload just for upload images, now I resize the image (maxWidth/maxHeight) in the backend.
The request now is to do it before upload the image, but with autoUpload set to true your method doesn't work because the filereader stop to read the file after the upload.

@bettysteger
Copy link
Author

@braindrained Sorry, I am still not sure what you mean.

Why do you need to upload a blob? If resizing the image in the backend you can just upload the image via autoUpload and that's it?

@braindrained
Copy link

@lpsBetty lol maybe I'm not explaining it properly (this is not my language :) ).

I've an application already working with the resize made in the backend.
The request by the customer is to do it before upload the image in order to not get the server too busy just in case of large amount of multiupload with large files.

@bettysteger
Copy link
Author

@braindrained and what do you use for cropping? just see this code for uploading a blob: https://gist.github.com/lpsBetty/2057e0bd0b4142d4070db0e1175ca47e

@braindrained
Copy link

@lpsBetty I've already tried your method but it does not work.
The problem is that with autoUpload set to true the onBeforeUploadItem is executed immediatly after the onAfterAddingFile and before that FileReader has ended to read the file, so the uploaded image is always the original and not the resized.

I resize the image this way:

uploader.onAfterAddingFile = function(item) {
    var reader = new FileReader();
    reader.onload = function (event) {
        var max_size = 1280;
        var tempImg = new Image();
        tempImg.src = reader.result;
        tempImg.onload = function () {
            if(this.width>max_size || this.height>max_size) {
                var canvas = document.createElement('canvas');
                canvas.width = max_size;
                canvas.height = max_size;
                var dimRatio = this.width / this.height;
                var padLeft = 0;
                var padTop = 0;
                if(dimRatio > 1) {
                    cropHeight = this.height;
                    cropWidth  = this.height;
                    padLeft = (this.width - this.height)/2;
                }
                if(dimRatio < 1) {
                    cropHeight = this.width;
                    cropWidth  = this.width;
                    padLeft = (this.height - this.width)/2;
                }

                document.body.appendChild(canvas);
                var ctx = canvas.getContext("2d");
                ctx.drawImage(this, padLeft, padTop, cropWidth, cropHeight, 0, 0, max_size, max_size);

                var dataURL = canvas.toDataURL("image/png", 1);
                var n = dataURL.indexOf(",");
                var data = dataURL.toString().substring(n+1);

                $scope.$apply(function () {
                    var ext = item.file.type.split("/")[1];
                    if (['jpg', 'jpeg', 'gif', 'png', 'pdf'].indexOf(ext) >= 0) {
                        var imgFile = b64toBlob(data,'image/png')
                        item._file = imgFile;
                        item.upload();
                    } else {
                        // invalid type
                    }
                });                    
            }
        };

    };
    reader.readAsDataURL(item._file);
};

@bettysteger
Copy link
Author

bettysteger commented Sep 6, 2016

@braindrained I think you do not need a $scope.$apply and item.upload() here in your code. Either use autoUpload=true or item.upload() ...
Isn't this code uploading the original file and the resized file?

I am not sure if this is the right place to discuss this, and I do not want to expand this closed issue!

@braindrained
Copy link

I agree, thanks anyway.

@RobbertWolfs
Copy link

Thanks for this thread. Exactly needed this solutions 💯

@josencv
Copy link
Collaborator

josencv commented Oct 18, 2016

For anyone who just wants to upload a blob file without extra data and does not care about compatibility with IE9 (and maybe IE10?) see #602

@thrinay
Copy link

thrinay commented Jun 21, 2017

I tried inserting the blob and didn't actually work with an existing image in the controller. I do will attach here the image of what the desired result should look like, and any advice or help is much more appreciated
Just the existing image should be dragged to the uploader. This is what the desired result is. Drag the image in the left corner to uploader
screenshot from 2017-05-24 11-41-29

@blueror815
Copy link

Hi @lpsBetty
I am going to edit existing photo of profile using angular-file-upload and img-crop.
Here is my code.

/**
                         * Show preview with cropping
                         */
                        uploader.onAfterAddingFile = function(item) {
                            $scope.croppedImage = '';
                            var reader = new FileReader();
                            reader.onload = function(event) {
                                $scope.$apply(function(){
                                    $scope.image = event.target.result;
                                });
                            };
                            reader.readAsDataURL(item._file);
                        };

                        /**
                         * Upload Blob (cropped image) instead of file.
                         * @see
                         *   https://developer.mozilla.org/en-US/docs/Web/API/FormData
                         *   https://github.com/nervgh/angular-file-upload/issues/208
                         */
                        uploader.onBeforeUploadItem = function(item) {
                            var blob = dataURItoBlob($scope.croppedImage);
                            item._file = blob;
                        };

                        /**
                         * Converts data uri to Blob. Necessary for uploading.
                         * @see
                         *   http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata
                         * @param  {String} dataURI
                         * @return {Blob}
                         */
                        var dataURItoBlob = function(dataURI) {
                            var binary = atob(dataURI.split(',')[1]);
                            var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
                            var array = [];
                            for(var i = 0; i < binary.length; i++) {
                                array.push(binary.charCodeAt(i));
                            }
                            return new Blob([new Uint8Array(array)], {type: mimeString});
                        };

                        /**
                         * @description save image for profile
                         */
                        $scope.saveImage = function() {

                            if ($scope.uploader.queue.length == 0) {
                                var blob = dataURItoBlob($scope.croppedImage);
                                var new_file = new File([blob], "default.png");
                                console.log("New File", new_file);

                                $scope.uploader.queue.push(new_file);

                                $scope.uploader.uploadItem(new_file);
                            } else {
                                $scope.uploader.uploadAll();
                            }
                        }

And my html is below.

<script type="text/ng-template" id="/selectImgModal.html">
    <div class="modal-header">
        <button type="button" data-dismiss="modal" aria-hidden="true" ng-click="cancel()" class="close">×</button>
        <h4  class="modal-title">{{'profile.SELECT_PROFILE_IMG' | translate}}</h4>
    </div>

    <div class="modal-body" style="min-height: 200px">
        <form>
            <input type="file" id="fileInput" nv-file-select="" uploader="uploader" />

            <div class="row">
                <div class="col-lg-7 col-sm-7 col-md-7">
                    <div class="cropArea">
                        <img-crop image="image" result-image="croppedImage"></img-crop>
                    </div>
                </div>

                <div class="col-lg-5 col-md-5 col-sm-7">
                    <h5 style="font-weight: bold">Your Profile Potrait</h5>
                    <div>
                        <img ng-src="{{croppedImage}}" />
                    </div>
                </div>

                <div class="row profile-img-btn-container">
                    <button type="button" class="btn btn-primary" ng-click="saveImage()">Save</button>

                    <button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
                </div>
            </div>
        </form>
    </div>
</script>

So in order to edit existing photo id, user will click photo id of user.
Then new modal to edit it will popup.
Here is my problem.
As you can see my angular code, i am making a File Object with blob and add it to queue and upload it.
But i am getting below error Msg.

service.js:115 TypeError: item._prepareToUploading is not a function
    at FileUploader.uploadItem (angular-file-upload.js:289)
    at ChildScope.SelectProfileImgController.$scope.saveImage (app.js:16017)
    at fn (eval at compile (service.js:115), <anonymous>:4:218)
    at expensiveCheckFn (service.js:115)
    at callback (service.js:115)
    at ChildScope.$eval (service.js:115)
    at ChildScope.$apply (service.js:115)
    at HTMLButtonElement.<anonymous> (service.js:115)
    at HTMLButtonElement.dispatch (service.js:115)
    at HTMLButtonElement.elemData.handle (service.js:115)

Could you please help me to fix this issue?

@bettysteger
Copy link
Author

bettysteger commented Jan 16, 2018

@blueror815 I don't know why you need this IF if ($scope.uploader.queue.length == 0) { ..

shouldn't the uploader always have an item in the queue, when an user selects an image?

and please use the latest solution: https://gist.github.com/lpsBetty/2057e0bd0b4142d4070db0e1175ca47e

@FURQAN2050
Copy link

hi betty...AM just getting stuck on this.
uploader.onAfterAddingFile = function (item) {
//upload file right after the user adds a file

       console.info('After adding a file', item);
       $scope.croppedImage = '';
          var reader = new FileReader();
          reader.onload = function(event) {
            $scope.$apply(function(){
              $scope.image = event.target.result;
              console.log('scope image',$scope.image)
            });
          };
          reader.readAsDataURL(item._file);
          console.log('item file',item._file);
           uploader.uploadAll(); 
    };
    // --------------------
    uploader.onAfterAddingAll = function (items) {
      console.info('After adding all files', items);
    };
    // --------------------
    uploader.onWhenAddingFileFailed = function (item, filter, options) {
      console.info('When adding a file failed', item);
    };
    // --------------------
    uploader.onBeforeUploadItem = function (item) {
      console.log('cropImage',$scope.croppedImage);
     var blob = dataURItoBlob($scope.croppedImage);
        item._file = blob;
      console.info('Before upload', item);
      
      
    };
    // --------------------
    uploader.onProgressItem = function (item, progress) {
      console.info('Progress: ' + progress, item);
    };
    // --------------------
    uploader.onProgressAll = function (progress) {
      console.info('Total progress: ' + progress);
    };
    // --------------------
    uploader.onSuccessItem = function (item, response, status, headers) {
      console.info('Success', response, status, headers);
      // $scope.$broadcast('uploadCompleted', item);
      // $scope.load();
    };
    // --------------------
    uploader.onErrorItem = function (item, response, status, headers) {
      console.info('Error', response, status, headers);
    };
    // --------------------
    uploader.onCancelItem = function (item, response, status, headers) {
      console.info('Cancel', response, status);
    };
    uploader.onCompleteItem = function (item, response, status, headers) {
      console.info('Complete', response, status, headers);
      console.log(response.result);

      // $scope.currentContact.imageName = response.result.files.file[0].name;
      // $scope.currentContact.field = response.result.files.file[0].field;
      // $scope.currentContact.container = response.result.files.file[0].container;
      // $scope.currentContact.originalFilename = response.result.files.file[0].originalFilename;
      // $scope.currentContact.size = response.result.files.file[0].size;
      // $scope.currentContact.type = response.result.files.file[0].type;
      // $scope.tempImage=response.result.files.file[0];
      //console.log('temp image contact',$scope.tempImage);

      console.log('current afterContact uploading an image', $scope.currentContact);
      //$scope.cropImage(response.result.files.file[0]);
    };
    uploader.onCompleteAll = function () {
      console.info('Complete all');
    };

    var dataURItoBlob = function(dataURI) {
      var binary = atob(dataURI.split(',')[1]);
      var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
      var array = [];
      for(var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i));
      }
      return new Blob([new Uint8Array(array)], {type: mimeString});
    };

HTML:



@FURQAN2050
Copy link

FURQAN2050 commented Jun 21, 2018

 <div style="  background: #E4E4E4;
                                overflow: hidden;
                                width:500px;
                                height:350px;"> <img-crop ng-show="image" image="image" result-image="croppedImage" area-type="square" result-image-size="250"></img-crop>
                                </div>
                                    <div><img ng-src="{{croppedImage}}" /></div>

@bettysteger
Copy link
Author

@FURQAN2050 can you please describe your problem?
It would help to know the problem, before reading and understanding your code ;)

I recommend using the latest solution from here!

@FURQAN2050
Copy link

FURQAN2050 commented Jun 23, 2018 via email

@FURQAN2050
Copy link

FURQAN2050 commented Jun 28, 2018 via email

@FURQAN2050
Copy link

FURQAN2050 commented Jun 28, 2018 via email

@bettysteger
Copy link
Author

@FURQAN2050 do you clear the queue after uploading? is there an error or what happens when you reupload the same file?

@FURQAN2050
Copy link

FURQAN2050 commented Jun 28, 2018 via email

@FURQAN2050
Copy link

FURQAN2050 commented Jun 28, 2018 via email

@FURQAN2050
Copy link

FURQAN2050 commented Jun 28, 2018 via email

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

No branches or pull requests