‫In this second part of uploading tour images,
‫we are now going to process all of them.
‫But before we start doing that,
‫there's actually something that I need to fix
‫in the user controller.
‫And so actually in this resizeUserPhoto,
‫and that is that we actually need
‫to await this whole operation here, all right?
‫So all of this here will actually return a promise,
‫and that makes sense, right,
‫because all of these operations here, they take some time,
‫and so of course they happen in the background.
‫So they are asynchronous code,
‫and so obviously they should not block the event loop.
‫Now the problem here is that right now
‫we're calling this next function here,
‫so the next middleware, without actually waiting
‫that these operations here finish.
‫And that is not a good idea.
‫And so let's simply use await here,
‫and then of course async, and all that stuff.
‫So catchAsync in here.
‫All right, give it a save, and that's much better.
‫And so now we're actually going to do something
‫very similar with our tour images.
‫And so actually let me go ahead and copy this.
‫All right, and although it's not going
‫to be exactly the same, it's good to already have it here.
‫So this one will also be an async function.
‫All right, now just as before,
‫in case there are no images uploaded,
‫then we want to move straight to the next middleware.
‫So if there are no request.files,
‫and actually I'm gonna take it one step further,
‫requiring that there is both the image cover
‫and also images.
‫And so basically we want to move to the next middleware
‫in case there is no request.files.imageCover,
‫or no request.files.images.
‫So in that case, next.
‫Okay, and now let's start by processing the cover image,
‫and then afterwards all of the other images
‫in a loop, basically.
‫So other images.
‫Now where do we actually get the cover image?
‫Well remember how I said that it is at request.files?
‫No longer file, and then .coverImage,
‫or actually imageCover.
‫And then remember how it was an array?
‫So let's take a look at that one more time.
‫Right, so all of this is request.files,
‫and then from there we take imageCover,
‫and that is an array of one element.
‫And so now we want the element number zero,
‫and then .buffer.
‫So this weird looking thing here.
‫Then we want to resize it to a 2/3 ratio,
‫and the width will be 2,000 pixels,
‫and the height 1,333, all right?
‫And so again, that's a nice 3/2 ratio,
‫which is very common in images.
‫Next, we also want to format it as a JPEG with 90% quality,
‫and then save it as a file.
‫But this time it is to image/tours,
‫and here let's actually define our filename separately.
‫All right, because we're actually going to need it again.
‫So imageCoverFilename,
‫and so once more we now need to create a unique filename.
‫So now it's tour, and then let's again put the ID
‫of the tour here, where before it was the ID of the user.
‫And so that is at request.params,
‫so remember that this route
‫will always contain the ID of the tour,
‫and so therefore it's on request.params.id.
‫Then again the timestamp, Date.now,
‫and then let's say that this one is the cover image, okay?
‫And the other ones will then get some numbers.
‫And then of course .jpeg, and so let's use that here.
‫So imageCoverFilename.
‫Okay, and now as one last step,
‫we actually need to make it possible
‫that our update tour handler
‫then picks up this image cover filename to update it
‫in the current tour document, okay?
‫So let's quickly take a look
‫at how the document is actually updated.
‫And so we're very simply using
‫this updateOne factory function.
‫And that one will actually simply update all of the data
‫that's in the body onto the new document.
‫So just to prove that to you right here.
‫So updateOne, it takes in the entire request.body, okay?
‫And so now the secret is to actually put
‫this image cover filename on request.body, all right?
‫So request.body.coverImage.
‫Oh and again, it's called imageCover instead,
‫should be equal to imageCoverFilename, okay, makes sense?
‫And of course it is called imageCover
‫because that is the name we have in our schema definition.
‫And so then when it's doing the update,
‫it will then match this field in the body
‫with the field in our database.
‫And actually we can refactor this a bit
‫because we don't really need this variable name here at all.
‫So we could simply put this filename here right on the body.
‫So put that here, and then use it here right away.
‫So we don't need this line of code at all.
‫Great, and now before moving on to the other images,
‫let's actually test it with what we already have
‫at this point.
‫So let's get just rid of this console.log,
‫and so when we now do the request,
‫then our cover image should already be uploaded
‫and be put in the tour document, okay?
‫So these other three images here of course not,
‫but at least the image cover
‫should now be working at this point,
‫at least if we did everything correct.
‫So let's see if it is somewhere here,
‫and images is still empty obviously.
‫But I'm searching now for the cover image, so where is that?
‫And here it is, so imageCover,
‫and so that looks a lot like the filename
‫that we just specified.
‫And let's actually try to reload our all tours page here,
‫and maybe our new tour's already here.
‫Ah, here we go, so that is the new mountain biker tour.
‫And this here is indeed the image that we just uploaded.
‫So beautiful.
‫Now if we try to open up this page,
‫then the other images are not here, all right?
‫So they are nowhere to be found,
‫and so of course that's what we're gonna do next.
‫All right.
‫So remember that our images here
‫are also an array, which then contain
‫all of these new file uploads.
‫And so let's now actually use a loop
‫to process each of them in one iteration.
‫So request.files.images.foreach.
‫And then our callback function
‫in which we get access to the current file.
‫All right, now here we do actually need
‫to create the current filename,
‫and you will see why in a second.
‫So filename equals, and then something very similar to this.
‫With the difference that now it is not cover,
‫but instead we want to call them one, two and three.
‫And so in our callback function
‫we actually also get access to the current index, okay?
‫So we need file, and I for index.
‫And so here it is not cover, but index plus one.
‫So plus one, and that's just because this index
‫is zero-based, all right?
‫Next up comes the processing step itself,
‫which is again very similar to this one.
‫And now we of course need to mark this as async as well.
‫All right, and down here it is now the filename.
‫Okay, now why do we actually need this filename?
‫Well we need it because now we need to push this filename
‫into request.body.images, and so that's the exact same logic
‫as we had here before on request.body.imageCover.
‫So remember that in our collection,
‫request.body.images is actually an array.
‫And so now we need to create that array
‫and start it as an empty array.
‫So request.body.images
‫as an empty array, and then in each iteration
‫we will then push the current filename
‫to this images array.
‫So request.body.images.push,
‫the filename, all right?
‫And with this we're almost done.
‫There's just one small problem,
‫which is the fact that we're actually
‫not using async await correctly here in this case,
‫so in this loop.
‫And that's because this async await here
‫is only inside of this callback function
‫of the foreach loop.
‫And so that will actually not stop the code
‫from moving right next to this line
‫where we then call the next middleware, okay?
‫So again, right now we are actually not awaiting
‫any of this here, again, because this async await
‫happens inside of the callback function
‫of one of these loop methods.
‫And we run into this kind of problem actually before,
‫but there is fortunately a solution for this,
‫because since this is an async function here,
‫it will return a new promise.
‫And so if we do a map,
‫we can then actually save an array of all of these promises.
‫And then if we have an array
‫we can use promise.all to await all of them.
‫And so with that we will then actually await
‫until all this code, and in this case,
‫until all this image processing is done,
‫and only then move on to the next line,
‫which is calling the next middleware
‫to really update the tour documents.
‫And if we didn't do that in this case,
‫it would really not work at all,
‫because without this pushing here,
‫the request.body.images would still be empty
‫when we call next.
‫And so therefore, none of these image names
‫would then be persisted into the document, okay?
‫So let's now use promise.all,
‫and actually I'm not even saving this array here
‫to any variable, instead I will use promise.all
‫right away on this.
‫So await Promise.all
‫on the array returning from this, okay?
‫So I hope that made sense.
‫So now let's actually log the body here to the console.
‫All right, and so with this we should now be ready
‫to upload all of our images, all right?
‫Let's go down here and try this again.
‫All right, so where is our images array?
‫Well, maybe it was somewhere up here, ah, here it is.
‫So I missed that somehow.
‫And that looks very promising, right?
‫So three nice images,
‫let's now take a look here at our page.
‫Ah, there they are, but not quite correct, right?
‫So it looks as if we simply uploaded
‫the cover image three times.
‫So let's take a look at Postman,
‫but the image names are actually correct,
‫and so there's something wrong in our processing there.
‫Let's take a look also at requests.body, oh,
‫and I forgot to actually put it here.
‫Okay, but I think it's not even necessary
‫because we just saw in Postman
‫that the filenames are actually correct.
‫So we don't need this one anymore.
‫Now let's just take a look at the filenames
‫that we just uploaded, so that's in public and tours.
‫And so here they are.
‫So this was probably the first one from the last test.
‫And so as you see, all of them are, indeed,
‫the exact same image.
‫So cover, one, two, and three
‫are the same images, and so that's not correct.
‫And I can see actually right away what the problem is.
‫So you see that we're actually reading, still,
‫request.files.imageCover,
‫and not the file from the current iteration of this loop.
‫Okay, so instead that should be file.buffer.
‫All right, so let's actually go ahead
‫and delete all of these.
‫All right, and simply try it again now.
‫So let's send that.
‫Names appear still to be correct,
‫but now when we reload this, let's see.
‫Ah, that looks correct.
‫Beautiful, beautiful.
‫You see the images are all nicely formatted,
‫even this one which was, remember,
‫in portrait and not even landscape,
‫but we basically cropped that and it still looks beautiful.
‫Okay, so here we can now confirm that,
‫so that it's actually in the 2,000s by 1,333 pixels.
‫All right, it's still a little bit on the big side here,
‫so maybe we could reduce the quality a little bit more,
‫but that's not really important.
‫What really matters here is that the logic
‫that we just applied here really works.
‫So just to quickly recap what we did in these two lectures.
‫So we created a multer upload using the memory storage
‫and this filter only for images.
‫Then we created the upload tour image middleware
‫by using upload.fields, which takes in one image cover
‫and three images, and then on the request
‫it will put the files property like this, all right?
‫Then in our next middleware we resize these images,
‫and first the cover image,
‫and then the remaining three images.
‫And what's really important to note here
‫is how we put the image filenames on request.body.
‫And we do that so that in the next middleware,
‫which is the actual route handler,
‫it will then put that data onto the new document
‫when it updates it, okay?
‫So we do that with the image cover,
‫and we also do that with the remaining images
‫by pushing it into body.images,
‫which as we know from our tour schema,
‫expects an array of strings.
‫And so in this case, filenames.
‫So about these other images,
‫we had them on request.files.images, so it's an array,
‫and so of course we loop through it using map.
‫And we use map so that we can basically save
‫the three promises which are the result
‫of these three async functions here,
‫so we can then await all of them here
‫using Promise.all, okay?
‫And only after that we then move on
‫to the actual tour update handler, okay?
‫And this part is really important,
‫so it's important that we only move on
‫to the next middleware as soon as this part here
‫is really completed.
‫Because otherwise, request.body.images will be empty,
‫and of course our filenames are then not gonna be saved
‫to the current tour document, all right?
‫And that actually wraps up the image
‫or file uploading part of this section.
‫So I hope that was fun,
‫I know that for me it was really cool.
‫So I really like this kind of stuff,
‫which makes my applications feel like really real world.
‫Anyway, hope you enjoyed it,
‫and I'll see you in the next one.