veliovgroup / Meteor-Files

🚀 Upload files via DDP or HTTP to ☄️ Meteor server FS, AWS, GridFS, DropBox or Google Drive. Fast, secure and robust.
https://packosphere.com/ostrio/files
BSD 3-Clause "New" or "Revised" License
1.11k stars 167 forks source link

How to use subversions #76

Closed justinFather closed 8 years ago

justinFather commented 8 years ago

Hi,

I tried as below

const Images = new FilesCollection({
  ......
  onAfterUpload: function( fileRef ) {
    const cropName = fileRef.path + "__thumbnail__.jpg"
    Imagemagick.crop({
      srcPath: fileRef.path,
      dstPath: cropName,
      width: 200,
      height: 200,
      quality: 0.8,
      gravity: "Center"
    })
    const upd = {
      $set: {
        "versions.thumbnail" : {
          path: cropName,
          type: "image/jpeg",
          extension: "jpg"
        } 
      }
    }
    return Images.update( fileRef._id, upd )
  }
  ......
})

I could see the thumnail file generated on the same directory with original one.

on client side code

this.images().map(( image ) => {
  return (
    <li key={image._id}>  
      <img src={Images.link(image, "thumbnail")} />
    </li>
  )
})

'scr' attribute looks like "......id/thumbnail/id.jpg" but the image shown up is not a 200x200 thumbnail. It is just original one.

dr-dimitru commented 8 years ago

Hi @justinFather, from the docs:

If requested subversion isn't found, original will be returned

It's probably caused by wrong path or you're requesting thumbnail before it actually created, or subversion record is inserted into MongoDB. Try to render template only after callback in .insert() method is triggered.

justinFather commented 8 years ago
versions:Object
  original:Object
    extension:"jpg"
    path:"assets/app/uploads/Images/B5C8sKwNxjjkoLr28.jpg"
    size:18744
    type:"image/jpeg"
    __proto__:Object
  thumbnail:Object
    extension:"jpg"
    path:"assets/app/uploads/Images/B5C8sKwNxjjkoLr28.jpg__thumbnail__.jpg"
    typs:"image/jpeg"
    __proto__:Object
dr-dimitru commented 8 years ago

"assets/app/uploads/Images/B5C8sKwNxjjkoLr28.jpg__thumbnail__.jpg" is wrong path. If you change it to: "assets/app/uploads/Images/B5C8sKwNxjjkoLr28__thumbnail__.jpg", it should work

dr-dimitru commented 8 years ago

If Imagemagick.crop() has a callback, you should update record in MongoDB there, after image is cropped.

justinFather commented 8 years ago

Even though I used Imagemagick.crop callback, onAfterUpload would return before the callback. I need something like 'promise'. I will try it.

dr-dimitru commented 8 years ago

Use fibers/future's .wait() method

justinFather commented 8 years ago

I did some experiment just before.

I copied all jpg in assets/app/uploads/Images/ to temp directory and deleted all jpg files and confirmed my app displayed broken image sign. and then I stopped my app and restore all jpg files to the directory and restart my app.

It shows thumnails 200x200( it still has the name B5C8sKwNxjjkoLr28.jpg__thumbnail__.jpg )

I see. I will try .wait()

dr-dimitru commented 8 years ago

I copied all jpg in assets/app/uploads/Images/ to temp directory and deleted all jpg files and confirmed my app displayed broken image sign. and then I stopped my app and restore all jpg files to the directory and restart my app.

So, it was the cache. I recommend to always disable cache in browser when working with this kind of libraries

justinFather commented 8 years ago

I think even though i use the cache, if i use .wait() properly, it will work fine. I will try it 1-2 hours later because now i'm not available to code now.

dr-dimitru commented 8 years ago

It's always better to use async code, then co-routines. If you need to show thumbnail as soon as it's available - set observer on the Client and wait until versions.thumbnail is added.

justinFather commented 8 years ago

Im back. :D

I read your last comment. I confirmed already that versions.thumbnail was added using console.log( image) before .link() call. Is it not enough?

And how about using Meteor.wrapAsync for Imagemagic.crop? I will test it now.

dr-dimitru commented 8 years ago

Meteor.wrapAsyncdirectly uses futures lib.

I read your last comment. I confirmed already that versions.thumbnail was added using console.log( image) before .link() call. Is it not enough?

  1. You log this on server?
  2. .link() called on client?
justinFather commented 8 years ago
  onAfterUpload: function( fileRef ) {
    const cropName = fileRef.path + "__thumbnail__.jpg"
    Meteor.wrapAsync( Imagemagick.crop({
      srcPath: fileRef.path,
      dstPath: cropName,
      width: 200,
      height: 200,
      quality: 0.8,
      gravity: "Center"
    }))
    const upd = {
      $set: {
        "versions.thumbnail" : {
          path: cropName,
          type: "image/jpeg",
          extension: "jpg"
        } 
      }
    }
    return Images.update( fileRef._id, upd )
  }

There was some improvement that I can see the thumbnail through pressing F5. Before that, I had to do as below to see the thumbnail.

I copied all jpg in assets/app/uploads/Images/ to temp directory and deleted all jpg files and confirmed my app displayed broken image sign. and then I stopped my app and restore all jpg files to the directory and restart my app.

dr-dimitru commented 8 years ago

post you Client's code here one more time please

justinFather commented 8 years ago

I'm using Meteor 1.3 + reactjs

below is reacjs code on client side

this.images().map(( image ) => {
  console.log( image )      // <---------------------- here
  return (
    <li key={image._id}>  
      <img src={Images.link(image, "thumbnail")} />
    </li>
  )
})
justinFather commented 8 years ago

client side upload part

  onChange(e) {
    e.preventDefault()
    const self = this
    _.forEach( e.target.files, function(file) {
      const img = new Image()
      img.onload = function() {
        const { width, height, rate, extension, type } = Meteor.settings.public.uploadConf
        const ratio = Math.sqrt( width * height / ( this.width * this.height ) )
        processImage( file, this.width * ratio, this.height * ratio, rate, function ( data ){
          self.state.currentImage.set( Images.insert({
            file: new File( [ Lib.dataURL2Array(data) ], `${file.name}.${extension}`, { type: type } ),
            onUploaded: function (error, fileObj) {
              if (error) {
                console.log('Error during upload: ' + error);
              } else {            
                //alert('File "' + fileObj.name + '" successfully uploaded');
              }
            },
            streams: 'dynamic',
            chunkSize: 'dynamic'
          }))
        })
      }
      img.src = URL.createObjectURL( file )
    })
  }
dr-dimitru commented 8 years ago

If this code is reactive you can do something like:

this.images().map(( image ) => {
  console.log( image )      // <---------------------- here
  return (
    <li key={image._id}>  
      <img src={(image.versions.thumbnail) ? Images.link(image, "thumbnail") : '/img/dummy_placeholder.png'} />
    </li>
  )
})
justinFather commented 8 years ago

Yes, mine is reactive!!

I will do it.

justinFather commented 8 years ago

It works great!!!

It's what i want!!!

Thank you very much.

dr-dimitru commented 8 years ago

BTW there is a little chance what callback of ImageMagick.crop() called before bytes is actually written to FS, for example after its resized in memory. I'm using gm via this lib and image available as soon as callback is triggered

dr-dimitru commented 8 years ago

It works great!!!

:) great!