#multer module
Explore tagged Tumblr posts
codeonedigest · 2 years ago
Video
youtube
Nodejs Javascript Application for File Upload and Download using Multer ... Full Video Link      https://youtu.be/RgQcWHiqOfc           Hello friends, new #video on #fileupload #filedownload #file #upload #download in #nodejs #javascript #project #application #tutorial #examples is published on #codeonedigest #youtube channel.  @java #java #aws #awscloud @awscloud @AWSCloudIndia #salesforce #Cloud #CloudComputing @YouTube #youtube #azure #msazure  #codeonedigest @codeonedigest  #nodejs  #shopifytutorialforbeginners #shopify #shopifytutorial #howtocreateashopifystore #shopifyaddproductstopage #shopifyaddproducttocollection #shopifyaddproductcategories #shopifyaddproductvariants #shopifyaddvariantstoexistingproduct #shopifyaddvariants #shopifyaddvarianttocollection #shopifyaddvariantsinbulk #shopifyupdateproduct #shopifychangeproductprice #shopifydeleteproduct #shopifydeleteproducttype #shopifychangeproductorder
1 note · View note
prachivermablr · 4 years ago
Link
0 notes
holytheoristtastemaker · 5 years ago
Quote
What Is Mongo? The name Mongo was taken from the word Humongous. In the context of storage it can mean dealing with large quantities of data. And that's what databases are pretty much for. They provide you with an interface to create, read, edit(update) and delete large sets of data (such as user data, posts, comments, geo location coordinates and so on) directly from your Node.js program. By "complete" I don't mean documentation for each Mongo function. Rather, an instance of Mongo code used as a real-world example when building a PWA. A Mongo collection is similar to what a table is in a MySQL database. Except in Mongo, everything is stored in a JSON-like object. But keep in mind that it also uses BSON to store unique _id property, similar to how MySQL stores unique row key. Installing Mongo And Basic Operations In the Terminal on Mac or bash.exe on Windows 10, log in to your remote host with ssh [email protected] (replace the x's with your web host's IP address.) Then use cd to navigate to your project directory on the remote host, for example, /dev/app. Install the system-wide Mongo service: sudo apt-get install -y mongodb The -y directive will automatically answer "yes" to all installation questions. We need to start the Mongo service after this (explained in next section.) Mongo Shell Just like MySQL has a command line shell where we can create tables, users, and so on, Mongo also has its own shell to create collections, show databases, and more. First we need to start the Mongo service: sudo service mongodb start If you ever need to stop the Mongo server, you can do that with: sudo service mongodb stop But don't stop it – we need the service running for the next part! To enter the Mongo shell, type the mongo command into your cli/bash: mongo You will see your bash change to a > character. Let's type the use command to create a new (or switch to an existing) database: > use users switched to db users Type > db to verify that we indeed switched to users database: > db users Let's take a look at all existing databases by executing the show dbs command: > show dbs admin 0.000GB local 0.000GB Even though we created a users database in the earlier step, it's not on this list. Why? That's because we haven't added any collections to the database. This means that users actually exists, but it won't be shown on this list until a collection is added. Adding Mongo Collection Let's add a new collection to users database.  Remember a collection in Mongo is the equivalent of a table in MySQL: db.users.insert({name:"felix"}) We just inserted a collection into the users database. Let's run > show dbs now: > show dbs admin 0.000GB local 0.000GB users 0.000GB Simply inserting a JSON-like object into the users database has created a "table." But in Mongo, it's just part of the document-based model. You can insert more objects into this object and treat them as you would rows/columns in MySQL. Installing the Mongo NPM Package npm install mongodb --save Generating The Data Before we go over Mongo methods, let's decide what we want to store in the database Let's take a look at world coordinate system. It's quite different from Cartesian. The central point here is located relatively near Lagos, Nigeria: Latitude and Longitude visualized. [0,0] point is located in the center of the coordinate system with axis going left and down in negative direction. HTML5 geo location will take care of calculating your location automatically based on your IP address. But we still need to convert it to 2D on the image. Image credit. Front-End Collecting the data. HTML5 provides an out-of-the-box geo location system and gives us latitude and longitude, as long as the client agrees to share that information. The following code will create a pop up message box on desktop and mobile devices asking users to "allow" the browser to share its location. If user agrees, it will be stored in lat and lon variables: if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { let lat = position.coords.latitude; let lon = position.coords.longitude; } } Well, that's a great start. But this isn't enough. We need to convert it into a 2D coordinate system that HTML5 understands. We need to draw location markers on top of the map image! So I wrote this simple World2Image function that takes care of that: function World2Image(pointLat, pointLon) { const mapWidth = 920; const mapHeight = 468; const x = ((mapWidth / 360.0) * (180 + pointLon)); const y = ((mapHeight / 180.0) * (90 - pointLat)); return [x, y]; } We simply divide map image dimensions by 360 and 180 respectively and then multiply them by 180 + pointLong and 90 - pointLat to adjust to the center. And our final code that converts latitude/longitude to 2D coordinates will look like this: if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { let lat = position.coords.latitude; let lon = position.coords.longitude; let xy = World2Image(lat, lon); let x = xy[0]; let y = xy[1]; } } In your app of course you can use any data you want. We just need a meaningful set to demonstrate a practical example for a potential live sunset photography app. Place the code above between tags within the tag in your PWA. Preferably inside: window.onload = event => { /* here */ }; Now every time a new visitor joins our page, they will be asked to share their geolocation. Once they press the "Allow" button, it will be collected and stored in lat, lon, x, and y vars. We can then create a Fetch API call to send it to our back-end server. API End-point Round Trip Below are the sections Front-End, Back-End, and Front-End again. Developing an API end-point (/api/user/get for example) you will often follow this pattern. First we need to call Fetch API to trigger an HTTP request to an end-point. Front-End API Here is the simple code that gives us two Fetch API requests. At this time we only need these two actions to build our geolocation API. Later in this article I'll show you how they connect to Mongo on the back-end. class User { constructor() { /* Currently unused */ this.array = []; } } // Make JSON payload let make = function(payload) { return { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }; } /* /api/add/user Send user's geolocation to mongo payload = { x : 0, y : 0, lat: 0.0, lon: 0.0 } */ User.add = function(payload) { fetch("/api/add/user", make(payload)) .then(promise => promise.json()).then(json => { if (json.success) console.log(`Location data was entered.`); else console.warn(`Location could not be entered!`); }); } /* /api/get/users Get geolocations of all users who shared it */ User.get = function(payload) { fetch("/api/get/users", make(payload)) .then(promise => promise.json()) .then(json => { if (json.success) console.log(`Location data was successfully fetched.`); else console.warn(`Users could not be fetched!`); }); } We can now use static User functions to make Fetch API calls. Of course, nothing is stopping you from simply calling the fetch function anywhere in your code wherever it makes sense. It's just that putting everything into a User object helps us organize code. Back-End This tutorial assumes you have Node and Express running. Showing how to set that up would take another whole tutorial. But here are the core API commands coded in Express. It's quite simple. The code below is from the express.js file, which is the complete SSL server. The only thing you need to change if you plan on implementing this on your remote host is generating your own SSL certificates with certbot and LetsEncrypt. Express, Multer and Mongo Init Shenanigans First we need to initialize everything: /* include commonplace server packages... */ const express = require('express'); const https = require('https'); const fs = require('fs'); const path = require('path'); /* multer is a module for uploading images */ const multer = require('multer'); /* sharp works together with multer to resize images */ const sharp = require('sharp'); /* instantiate the express app... */ const app = express(); const port = 443; // Create Mongo client const MongoClient = require('mongodb').MongoClient; // Multer middleware setup const storage = multer.diskStorage({ destination: function (req, file, cb) {cb(null, './sunsets')}, filename: function (req, file, cb) {cb(null, file.fieldname + '-' + Date.now())} }); // Multer will automatically create upload folder ('./sunsets') const upload = multer( { storage : storage } ); // body-parser is middleware included with express installation. The following 3 lines are required to send POST body; if you don't include them, your POST body will be empty. const bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({extended: true})); app.use(bodyParser.json()); // Create some static directories. A static folder on your site doesn't actually exist. It's just an alias to another folder. But you *can also map it to the same name. Here we're simply exposing some folders to the front end. app.use('/static', express.static('images')); app.use('/sunsets', express.static('sunsets')); app.use('/api', express.static('api')); app.use('/static', express.static('css')); app.get('/', function(req, res) { res.sendFile(path.join(__dirname + '/index.html')); }); // General use respond function -- send json object back to the browser in response to a request function respond( response, content ) { const jsontype = "{ 'Content-Type': 'application/json' }"; response.writeHead(200, jsontype); response.end(content, 'utf-8'); } // Utility function...convert buffer to JSON object function json( chunks ) { return JSON.parse( Buffer.concat( chunks ).toString() ) } We still need to add end points to the express.js file. The code below is assumed to go into the same file just underneath what we've written above. Setting up API end-points Creating POST API end-points with Express is a breeze. The code below will be triggered on the server when we use fetch calls we talked about earlier (using the User object and its static methods) from client-side. API End-point 1 /api/add/user This server side end-point will add geolocation data to Mongo database: // End-point: api/add/user // POST method route - Add geolocation to Mongo's Photographer collection app.post('/api/add/user', function(req, res, next) { const ip = req.connection.remoteAddress; const { x, y, lat, lon } = req.body; // Connect to mongo and insert this user if doesn't already exist MongoClient.connect(`mongodb://localhost/`, function(err, db) { const Photographer = db.collection('Photographer'); // Check if this user already exists in collection Photographer.count({ip:ip}).then(count => { if (count == 1) { console.log(`User with ${ip} already exists in mongo.`); db.close(); } else { console.log(`User with ${ip} does not exist in mongo...inserting...`); let user = { ip: ip, x: x, y: y, lat: lat, lon: lon }; // Insert geolocation data! Photographer.insertOne(user, (erro, result) => { if (erro) throw erro; // console.log("insertedCount = ", result.insertedCount); // console.log("ops = ", result.ops); db.close(); }); } }); res.end('ok'); }); }); API End-point 2 /api/get/users This server side end-point will grab a list in JSON format of all currently stored geolocations in the Mongo database: // End-point: api/get/users // POST method route - Get all geolocations from Mongo's Photographer collection app.post('/api/get/users', function(req, res, next) { // Connect to Mongo server MongoClient.connect(`mongodb://localhost/`, function(err, db) { const Photographer = db.collection('Photographer'); Photographer.find({}, {}, function(err, cursor) { // NOTE: You must apply limit() to the cursor // before retrieving any documents from the database. cursor.limit(1000); let users = []; // console.log(cursor); cursor.each(function(error, result) { if (error) throw error; if (result) { // console.log(result); let user = { x: result.x, y: result.y, lat: result.lat, lon: result.lon }; // console.log("user["+users.length+"]=", user); users.push(user); } }); // A more proper implementation: (WIP) (async function() { const cursor = db.collection("Photographer").find({}); while (await cursor.hasNext()) { const doc = await cursor.next(); // process doc here } })(); setTimeout(time => { const json = `{"success":true,"count":${users.length},"userList":${JSON.stringify(users)}}`; respond(res, json); }, 2000); }); }); }); API End-point 3 This will upload an image from an HTML form via the Node multer package: // End-point: api/sunset/upload // POST method route - Upload image from a form using multer app.post('/api/sunset/upload', upload.single('file'), function(req, res, next) { if (req.file) { // console.log("req.file.mimetype", req.file.mimetype); // const { filename: image } = req.file.filename; let ext = req.file.mimetype.split('/')[1]; let stamp = new Date().getTime(); // console.log("ext=",ext); output = `./sunsets/sunset-${stamp}.${ext}`; output2 = `https://www.infinitesunset.app/sunsets/sunset-${stamp}.${ext}`; console.log("output=",output); // console.log("output2=",output2); sharp(req.file.path) .resize(200).toFile(output, (err, info) => { // console.log(err); // console.log(info.format); }); // fs.unlinkSync(req.file.path); res.end(`Picture uploaded! Go Back`,`utf-8`); } // req.file is the `avatar` file // req.body will hold the text fields, if there were any res.end(`Something went wrong.`,`utf-8`); }); MongoDB Command Overview Primarily we will use .insertOne and .find Mongo collection methods. When inserting a location entry, we will test if that entry already exists in the database. If it does, there is nothing to do. If it doesn't, we'll insert a new entry. You can also use Mongo's .count method to count the number of entries in the document that match a particular document filter. To do any Mongo operations, we first need to connect to our Mongo server from within of our Node application. This is done using the .connect method. .connect MongoClient.connect(mongodb://localhost/, function(err, db) { /* mongo code */ }; db.close() When we're done we need to close the database connection: MongoClient.connect(mongodb://localhost/, function(err, db) { db.close(); }; .insertOne Photographer.insertOne(user, (erro, result) => { if (error) throw error; // console.log("insertedCount = ", result.insertedCount); // console.log("ops = ", result.ops); Don't forget to close db db.close(); }); Here, results.ops will display the object that was inserted. .find The find method produces something called a cursor in Mongo. This cursor is an object that you can call .each on to iterate over all found items. As per the Mongo documentation, you should always set a limit first with cursor.limit(1000); Photographer.find({}, {}, function(err, cursor) { // NOTE: You must apply limit() to the cursor // before retrieving any documents from the database. cursor.limit(1000); let users = []; // console.log(cursor) is [Cursor object]; cursor.each(function(error, result) { // check for errors if (error) throw error; // get data let x = result.x; let y = result.y; /* etc... */ } } Front-End Displaying the data Now that we have a basic Mongo API running on our back-end, we can send data back to the front-end and the client side needs to visually display it on the screen. I've chosen to use HTML5 to draw the coordinates as images. To draw images on canvas, you first have to include them in your HTML document as regular images with tags. Let's include the images in the HTML to cache them in the browser: /* Load images but move them offscreen */ #you, #world, #mark { position: absolute; top: -1000px; left: -1000px } And now the core of our front-end is finished. This call will do a bunch of things: see if device browser supports the HTML5 geolocation property, create a 2D canvas and draw world map image on it, get HTML5 geolocation data, send it to Mongo using Fetch API, make another call to get all previously inserted items into our Mongo db on the back-end and animate canvas by drawing markers based on returned JSON object: // Create payload -- redundant everywhere, // because all POST payloads are JSON // now in neat make() function let make = function(payload) { return { method: 'post', headers: { 'Accept': 'application/json' }, body: JSON.stringify(payload) }; }); // safe entry point, all images loaded etc. window.onload = function(event) { // a place to store location markers window.markers = []; // Because this is { window.User = User; } // create 2D canvas const c = document.getElementById("canvas"); const ctx = c.getContext("2d"); // get our images const world = document.getElementById("world"); const here = document.getElementById("here"); const marker = document.getElementById("marker"); if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { let lat = position.coords.latitude; let lon = position.coords.longitude; let xy = World2Image(lat, lon); let x = xy[0]; let y = xy[1]; console.log(`You are at ${lat} x ${lon} or = ${x} x ${y}`); // Add this user to mongo User.add({ x : x, y : y, lat: lat, lon: lon }); // get our user locations fetch("/api/get/users", make({})).then( promise => promise.json()).then(json => { /* Store returned list of markers */ window.markers = json.userList; // optionally debug each entry: json.userList.forEach(photographer => { // console.log(photographer); }); // Remove the loading bar window["loading"].style.display = 'none'; // display number of items returned console.log("json.userList.length", json.userList.length); }); // animation loop that draws all locations in real time function loop() { if (here && ctx) { // draw the world map ctx.drawImage(world, 0, 0, 920, 468); ctx.save(); // move to the center of the canvas ctx.translate(x, y); // draw the image ctx.drawImage(here, -here.width/2, -here.width/2); // draw marker images ctx.translate(-x, -y); if (window.cameras) { window.cameras.forEach(cam => ctx.drawImage(polaroid, cam.x-polaroid.width/2, cam.y-polaroid.width/2)); } ctx.restore(); // restore context } requestAnimationFrame(loop); } requestAnimationFrame(loop); }); } else msg.innerHTML = "Geolocation is not supported by this browser."; } Note: You don't have to render your data in this exact way. If you are using the React library, just go ahead and use components. It doesn't matter. Here I used vanilla JavaScript just for this example. What matters are the fetch requests and that we are rendering the returned JSON object containing location data on the screen. You don't have to use canvas. You could have used just the div elements for each marker. Open the live demo of this tutorial at infinitesunset.app. I open-sourced this entire project on GitHub Check complete source code for this Express.js SSL MongoDB server. This is a good starting point for someone who's trying to get Mongo to work on a remote host. ⭐Star it, 🍴 fork it, ⤵️download it or ✂️copy and paste it. End Result ( also see it running live at www.InfiniteSunset.app ) HTML5 geo location data visually displayed after being loaded from MongoDB.Thanks for reading, I hope you found this Mongo tutorial insightful.
http://damianfallon.blogspot.com/2020/04/the-complete-mongodb-pwa-tutorial.html
0 notes
mbaljeetsingh · 7 years ago
Text
File Upload With Multer in Node.js and Express
When a web client uploads a file to a server, it is generally submitted through a form and encoded as multipart/form-data. Multer is middleware for Express and Node.js that makes it easy to handle this multipart/form-data when your users upload files. In this tutorial, I'll show you how to use this library to handle different file upload situations in Node.
How Does Multer Work?
As I said above, Multer is Express middleware. Middleware is a piece of software that connects different applications or software component. In Express, middleware processes and transforms incoming requests to the server. In our case, Multer acts as a helper when uploading files.
Project Setup
We will be using the Node Express framework for this project. Of course, you'll need to have Node installed. 
Create a directory for our project, navigate into the directory and issue npm init to create a .json file that manages all the dependencies for our application.
mkdir upload-express cd upload-express npm init
Next, install Multer, Express and the other necessary dependencies necessary to bootstrap an express app.
npm install express multer body-parser --save
Next, create a server.js file. 
touch server.js
Then, in server.js, we will initialize all the modules, create an express app and create a server for connecting to browsers.
// call all the required packages const express = require('express') const bodyParser= require('body-parser') const multer = require('multer'); app.use(bodyParser.urlencoded({extended: true})) const app = express() //CREATE EXPRESS APP const app = express(); //ROUTES WILL GO HERE app.get('/', function(req, res) { res.json({ message: 'WELCOME' }); }); app.listen(3000, () => console.log('Server started on port 3000'));
Running node server.js and navigating to localhost:3000 on your browser should give you the following message.
Create the Client Code
The next thing will be to create an index.html file to write all the code that will be served to the client.
touch index.html
This file will contain the different forms that we will use for uploading our different file types.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>MY APP</title> </head> <body> <!-- SINGLE FILE --> <form action="/uploadfile" enctype="multipart/form-data" method="POST"> <input type="file" name="myFile" /> <input type="submit" value="Upload a file"/> </form> <!-- MULTIPLE FILES --> <form action="/uploadmultiple" enctype="multipart/form-data" method="POST"> Select images: <input type="file" name="myFiles" multiple> <input type="submit" value="Upload your files"/> </form> <!-- PHOTO--> <form action="/upload/photo" enctype="multipart/form-data" method="POST"> <input type="file" name="myImage" accept="image/*" /> <input type="submit" value="Upload Photo"/> </form> </body> </html>
Open server.js and write a GET route that renders the index.html file instead of the "WELCOME" message. 
// ROUTES app.get('/',function(req,res){ res.sendFile(__dirname + '/index.html'); });
Multer storage
The next thing will be to define a storage location for our files. Multer gives the option of storing files to disk as shown below. Here we set up a directory where all our files will be saved and we'll also give the files a new identifier.
//server.js // SET STORAGE var storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, 'uploads') }, filename: function (req, file, cb) { cb(null, file.fieldname + '-' + Date.now()) } }) var upload = multer({ storage: storage })
Handling File Uploads
Uploading a Single File
In the index.html file, we defined an action attribute that performs a POST request. Now we need to create an endpoint in the Express application. Open the server.js file and add the following code:
app.post('/uploadfile', upload.single('myFile'), (req, res, next) => { const file = req.file if (!file) { const error = new Error('Please upload a file') error.httpStatusCode = 400 return next(error) } res.send(file) })
Note that the name of the file field should be the same as the myFile argument passed to the upload.single function.
Uploading Multiple Files
Uploading multiple files with Multer is similar to a single file upload but a few changes.
//Uploading multiple files app.post('/uploadmultiple', upload.array('myFiles', 12), (req, res, next) => { const files = req.files if (!files) { const error = new Error('Please choose files') error.httpStatusCode = 400 return next(error) } res.send(files) })
Uploading Images
Instead of saving uploaded images to the file system, we will store them in a MongoDB database so that we can retrieve them later as needed. But first, let's install MongoDB.
npm install mongodb --save
We will then connect to MongoDB through the  Mongo.client method and then add the MongoDB URL to that method. You can use a cloud service like Mlab which offers a free plan or simply use the locally available connection.
const MongoClient = require('mongodb').MongoClient const myurl = 'mongodb://localhost:27017'; MongoClient.connect(myurl, (err, client) => { if (err) return console.log(err) db = client.db('test') app.listen(3000, () => { console.log('listening on 3000') }) })
Open server.js and define a POST request that enables saving images to the database.
app.post('/uploadphoto', upload.single('picture'), (req, res) => { var img = fs.readFileSync(req.file.path); var encode_image = img.toString('base64'); // Define a JSONobject for the image attributes for saving to database var finalImg = { contentType: req.file.mimetype, image: new Buffer(encode_image, 'base64') }; db.collection('quotes').insertOne(finalImg, (err, result) => { console.log(result) if (err) return console.log(err) console.log('saved to database') res.redirect('/') }) })
In the above code, we first encode the image to a base64 string, construct a new buffer from the base64 encoded string and then save it to our database collection in JSON format.
We then display a success message and redirect the user to the index page.
Retrieving Stored Images
To retrieve the stored images, we perform a mongodb search using the find  method and return an array of results.  We then go on and obtain the _id attributes of all the images and return them to the user.
app.get('/photos', (req, res) => { db.collection('mycollection').find().toArray((err, result) => { const imgArray= result.map(element => element._id); console.log(imgArray); if (err) return console.log(err) res.send(imgArray) }) });
Since we already know the id's of the images, we can view an image by passing its id in the browser as illustrated below.
app.get('/photo/:id', (req, res) => { var filename = req.params.id; db.collection('mycollection').findOne({'_id': ObjectId(filename) }, (err, result) => { if (err) return console.log(err) res.contentType('image/jpeg'); res.send(result.image.buffer) }) })
Conclusion
I hope you found this tutorial helpful. File upload can be an intimidating topic, but it doesn't have to be hard to implement. With Express and Multer, handling multipart/form-data is easy and straight forward. You can find the full source code for the file upload example in our GitHub repo.
via Envato Tuts+ Code https://ift.tt/2Dc2PJw
0 notes
atelierhaiko · 16 years ago
Video
tumblr
Médiation extra-muros du Centre International d’art et du Paysage avec les modules de Hans Walter Multer. Ici, installation de la sphère au sein d’une école primaire. Les petits étaient fous de joie, un immense plaisir de les voir sauter, s’allonger, jouer entre-eux, tout c’est passé à merveille!
0 notes
mbaljeetsingh · 8 years ago
Text
Ionic Image Upload and Management with Node.js – Part 2: Ionic App
This is the second post of our Ionic Image upload series! Last time we were building the Node.js backend and API routes, now it’s time to actually use them from a real Ionic app to upload images, display a list of images and delete single images!
This means we will implement a few views and make HTTP calls to our API, capture images from the camera or library and send those images with the FileTransfer plugin to our backend including an optional description for each image.
If you haven’t followed the first part, I recommend you check it out now as you otherwise don’t have a working backend.
Part 1: Ionic Image Upload and Management with Node.js – Server
Once we are done, our final app will look like in the animation below, which means we can upload the image, see a nice list of images and even open a bigger view for each image. So this article contains everything you need to build your own Ionic image sharing app!
If you feel lazy, you can also grab the code for the frontend app directly below.
Setting up a new Ionic App
Like always we start with a blank new Ionic app so everyone can follow all steps. We need a provider for our backend interaction and a few additional pages. Also, we install the camera plugin and the file transfer plugin to easily upload images with their Cordova and NPM packages. Go ahead and run:
ionic start devdacticImageUpload blank cd devdacticImageUpload ionic g provider images ionic g page home ionic g page uploadModal ionic g page previewModal ionic cordova plugin add cordova-plugin-camera ionic cordova plugin add cordova-plugin-file-transfer npm install --save @ionic-native/camera @ionic-native/file-transfer
Initially I tried the new HttpClient of Angular 4.3 but as it’s not yet (at the time writing this) a dependency of Ionic we would have to change a lot so we will stick with this solution for now. If you come from the future and want to see that way, just leave a comment below!
To use all of our stuff we need to import it to our module so change your src/app/app.module.ts to:
import { BrowserModule } from '@angular/platform-browser'; import { ErrorHandler, NgModule } from '@angular/core'; import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular'; import { SplashScreen } from '@ionic-native/splash-screen'; import { StatusBar } from '@ionic-native/status-bar'; import { MyApp } from './app.component'; import { ImagesProvider } from '../providers/images/images'; import { HttpModule } from '@angular/http'; import { Camera } from '@ionic-native/camera'; import { FileTransfer } from '@ionic-native/file-transfer'; @NgModule({ declarations: [ MyApp ], imports: [ BrowserModule, HttpModule, IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp ], providers: [ StatusBar, SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, ImagesProvider, Camera, FileTransfer ] }) export class AppModule {}
That’s all for the setup process, we are now ready to make our HTTP calls to our backend with our provider and later use the Cordova plugins.
Provider for Backend Interaction
It’s always recommended to put stuff like backend interaction into a provider so we created a new one for all of our image actions.
Inside this ImagesProvider we have 3 functions to make use of the backend API routes. Getting a list of images and deleting images is just one line as it’s a simple GET or DELETE request using the Http provider of Angular.
Uploading an image file is a bit more complex, but just because we need a few more information at this point. To upload our file using the fileTransfer we need the path to the image, the backend URL and some optional options. In this case we specify the fileKey to be ‘image‘ as this is what the Multer plugin on the Node.js side expects (as we have told it so).
We also wanted to pass some information along which is the params object of the options where we set the desc key to the value the user entered before. Once all of this is done, it’s just calling upload() and the file will be send to our server!
Now open your src/providers/images/images.ts and change it to:
import { Injectable } from '@angular/core'; import 'rxjs/add/operator/map'; import { Http } from '@angular/http'; import { FileTransfer, FileUploadOptions, FileTransferObject } from '@ionic-native/file-transfer'; @Injectable() export class ImagesProvider { apiURL = 'http://localhost:3000/'; constructor(public http: Http, private transfer: FileTransfer) { } getImages() { return this.http.get(this.apiURL + 'images').map(res => res.json()); } deleteImage(img) { return this.http.delete(this.apiURL + 'images/' + img._id); } uploadImage(img, desc) { // Destination URL let url = this.apiURL + 'images'; // File for Upload var targetPath = img; var options: FileUploadOptions = { fileKey: 'image', chunkedMode: false, mimeType: 'multipart/form-data', params: { 'desc': desc } }; const fileTransfer: FileTransferObject = this.transfer.create(); // Use the FileTransfer to upload the image return fileTransfer.upload(targetPath, url, options); } }
Our API and provider is now ready, so let’s put all of this into action!
Loading Images from the Backend
The first view of our app will display a list of images which we received from the backend side. We just need to call getImages() on our provider and we will get the data back inside the completion block, which we will then use inside the view in the next step.
Additional our first view has the open to delete an image (again just one call) and if we want to open an image for a bigger view of the actual image, we will open a Modal Controller with the PreviewModalPage which we implement later.
Finally the image upload will start with the presentActionSheet() so the user can decide if he wants to use an image from the library or capture a new one. After selecting, the takePicture() uses the Cordova plugin with the correct options to receive a URL to the file on the device.
At this point we could already upload the file, but we want to add an additional caption and perhaps if you want add options to change the image (just like you might know from Instagram) so we show the UploadModalPage which will then take care of calling the final upload function.
Also, once this modal is dismissed we reload our list of images if a special parameter reload was passed back from the modal, which means the upload finished and a new image should be shown inside the list!
Go ahead and change your src/pages/home/home.ts to:
import { ImagesProvider } from './../../providers/images/images'; import { Component } from '@angular/core'; import { IonicPage, NavController, ModalController, ActionSheetController } from 'ionic-angular'; import { Camera, CameraOptions } from '@ionic-native/camera'; @IonicPage() @Component({ selector: 'page-home', templateUrl: 'home.html', }) export class HomePage { images: any = []; constructor(public navCtrl: NavController, private imagesProvider: ImagesProvider, private camera: Camera, private actionSheetCtrl: ActionSheetController, private modalCtrl: ModalController) { this.reloadImages(); } reloadImages() { this.imagesProvider.getImages().subscribe(data => { this.images = data; }); } deleteImage(img) { this.imagesProvider.deleteImage(img).subscribe(data => { this.reloadImages(); }); } openImage(img) { let modal = this.modalCtrl.create('PreviewModalPage', { img: img }); modal.present(); } presentActionSheet() { let actionSheet = this.actionSheetCtrl.create({ title: 'Select Image Source', buttons: [ { text: 'Load from Library', handler: () => { this.takePicture(this.camera.PictureSourceType.PHOTOLIBRARY); } }, { text: 'Use Camera', handler: () => { this.takePicture(this.camera.PictureSourceType.CAMERA); } }, { text: 'Cancel', role: 'cancel' } ] }); actionSheet.present(); } public takePicture(sourceType) { // Create options for the Camera Dialog var options = { quality: 100, destinationType: this.camera.DestinationType.FILE_URI, sourceType: sourceType, saveToPhotoAlbum: false, correctOrientation: true }; // Get the data of an image this.camera.getPicture(options).then((imagePath) => { let modal = this.modalCtrl.create('UploadModalPage', { data: imagePath }); modal.present(); modal.onDidDismiss(data => { if (data && data.reload) { this.reloadImages(); } }); }, (err) => { console.log('Error: ', err); }); } }
When we get the initial list of images it will be empty, but later after uploading some images the response will look somehow like this:
[ { "_id": "59a409348068343d9b187d6b", "desc": "my description", "originalName": "ionic-roadmap-icon.png", "filename": "image-1503922484703", "created": "2017-08-28T12:14:44.709Z", "url": "http://localhost:3000/images/59a409348068343d9b187d6b" }, { "_id": "59a4093c8068343d9b187d6c", "desc": "Amrum!", "originalName": "18645529_1896463043974492_8029820227527114752_n.jpg", "filename": "image-1503922492005", "created": "2017-08-28T12:14:52.009Z", "url": "http://localhost:3000/images/59a4093c8068343d9b187d6c" } ]
We now build the view around this response by creating items for each entry and directly using the image URL of the JSON object. Remember, this is the URL we created inside the backend after receiving the entries, so this URL is not stored inside the database!
You might change URLs or backends so it wouldn’t be a good idea to store a absolute URL inside the database, but now that we get it we are happy on the frontend side as there is no additional work required to show images.
We can click each item to open the full screen preview, and sliding the items allows us to use the delete button for each image.
To upload a new image we add an ion-fab button which will float above our content and open the action sheet with the options for library or camera.
Now change your src/pages/home/home.html to:
<ion-header> <ion-navbar color="primary"> <ion-title>Images</ion-title> </ion-navbar> </ion-header> <ion-content> <h3 [hidden]="images.length !== 0" text-center>No Images found!</h3> <ion-list> <ion-item-sliding *ngFor="let img of images"> <ion-item tappable (click)="openImage(img)"> <ion-thumbnail item-start> <img [src]="img.url"> </ion-thumbnail> <h2></h2> <button ion-button clear icon-only item-end> <ion-icon name="arrow-forward"></ion-icon></button> </ion-item> <ion-item-options side="right"> <button ion-button icon-only color="danger" (click)="deleteImage(img)"> <ion-icon name="trash"></ion-icon> </button> </ion-item-options> </ion-item-sliding> </ion-list> <ion-fab right bottom> <button ion-fab (click)="presentActionSheet()"><ion-icon name="camera"></ion-icon></button> </ion-fab> </ion-content>
You can now run your app already but as you might have not uploaded anything (you could already upload files with Postman!) you will see a blank screen, so let’s work on the upload page now.
Capture & Upload Image to Node.js Server
Most of the logic is already implemented so we just need some more simple views.
The image upload view appears after the user has taken or selected an image. Now we use the data that was passed to our modal page and implement a simple dismiss()function to close the preview and the saveImage() function which will upload the actual image with the caption which the user can also enter on this page. Once the upload is finished, we dismiss the page but pass the reload parameter so our HomePage knows the image list needs to be reloaded.
Open your src/pages/upload-modal/upload-modal.ts and change it to:
import { ImagesProvider } from './../../providers/images/images'; import { Component } from '@angular/core'; import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular'; @IonicPage() @Component({ selector: 'page-upload-modal', templateUrl: 'upload-modal.html', }) export class UploadModalPage { imageData: any; desc: string; constructor(public navCtrl: NavController, private navParams: NavParams, private viewCtrl: ViewController, private imagesProvider: ImagesProvider) { this.imageData = this.navParams.get('data'); } saveImage() { this.imagesProvider.uploadImage(this.imageData, this.desc).then(res => { this.viewCtrl.dismiss({reload: true}); }, err => { this.dismiss(); }); } dismiss() { this.viewCtrl.dismiss(); } }
Inside the view for this page we only need the image and the inout field for the description, so really nothing special here. Go ahead and change your src/pages/upload-modal/upload-modal.html to:
<ion-header> <ion-navbar color="primary"> <ion-buttons start> <button ion-button icon-only (click)="dismiss()"><ion-icon name="close"></ion-icon></button> </ion-buttons> <ion-title>Upload Image</ion-title> </ion-navbar> </ion-header> <ion-content padding> <img [src]="imageData" style="width: 100%"> <ion-item> <ion-input placeholder="My Awesome Image" [(ngModel)]="desc"></ion-input> </ion-item> <button ion-button full icon-left (click)="saveImage()"> <ion-icon name="checkmark"></ion-icon> Save & Upload Image </button> </ion-content>
You are now ready to upload images and should see the new images inside your list! Let’s finish this up with the special image preview.
Opening Images Fullscreen
Of course the list image is a bit small so we want to be able to show the full image with the description and perhaps also the creation date (which is added by the server automatically).
The class of this view is just making use of the passed image and has a dismiss function, so change your src/pages/preview-modal/preview-modal.ts now to:
import { Component } from '@angular/core'; import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular'; @IonicPage() @Component({ selector: 'page-preview-modal', templateUrl: 'preview-modal.html', }) export class PreviewModalPage { img: any; constructor(public navCtrl: NavController, public navParams: NavParams, private viewCtrl: ViewController) { this.img = this.navParams.get('img'); } close() { this.viewCtrl.dismiss(); } }
For the view we need to get a bit tricky as we want a slightly transparent background which we will achieve with some CSS classes. Also, whenever the user clicks on the background we want to catch that event and hide the full screen image again.
Below the image we also display a card with the description and date, everything is directly retrieved from the JSON object of that image.
Now change your src/pages/preview-modal/preview-modal.html to:
<ion-content> <div class="image-modal transparent"> <ion-item class="close-fake" no-lines (click)="close()"> <ion-icon name="close"></ion-icon> </ion-item> <img [src]="img.url" class="fullscreen-image transparent" (click)="close()"> <ion-card> <ion-card-content class="img-info" *ngIf="img.desc"> <div class="desc-text">""</div> <p> <ion-icon name="calendar" item-left *ngIf="img.created"></ion-icon> </p> </ion-card-content> </ion-card> </div> </ion-content>
The last missing piece is the CSS to make the page look transparent, so open the src/pages/preview-modal/preview-modal.scss and change it to:
page-preview-modal { .img-info { padding-left: 20px; background: #fff; } .desc-text { font-weight: 500; font-size: larger; padding-bottom: 15px; color: color($colors, dark); } .content-ios { background: rgba(44, 39, 45, 0.84) !important; } .content-md { background: rgba(44, 39, 45, 0.84) !important; } .close-fake { background: transparent; color: color($colors, light); font-size: 3rem; } }
Woah, we are done! Finally..
To completely use your app you need to deploy it to a device as we are using Cordova plugins to capture images and for the upload process. If you already have data inside your database you should anyway be able to run the app inside your browser to see the list of images and also open the full screen preview!
Conclusion
Inside this 2 part series you have learnt to build a Node.js backend with MongoDB and also an Ionic image sharing App which makes use of our own API and uploads media files to a server.
You are now more or less ready to build the next Instagram or simply an image sharing app between you and your friends. Whatever it is that you build, let us know in the comments (and also if you enjoyed this series style with backend coding!).
You can also find a video version of this article below.
Happy Coding, Simon
The post Ionic Image Upload and Management with Node.js – Part 2: Ionic App appeared first on Devdactic.
via Devdactic http://ift.tt/2kVWp9S
0 notes
mbaljeetsingh · 8 years ago
Text
File Uploads in Angular with a Node and Hapi Backend
In this article, we will talk about how to handle file uploads with Angular. We will create an images uploader that allow user to upload single or multiple images file by drag and drop or select file dialog.
We will then upload the selected images and display them accordingly. We will also learn to filter the upload file type, for example, we only allow images, do not allow file type like PDF.
Sourcecode: http://ift.tt/2p20lqz
Demo: http://ift.tt/2o0mYH6
File Upload UI & API
File upload consists of two parts: the UI (front-end) and the API (back-end). We will be using Angular to handle the UI part. We need a backend application to accept the uploaded files. You may follow the backend tutorials or download and run either one of these server side application to handle file upload for your backend:-
File upload with Hapi.js: http://ift.tt/2m8mJxf, or
File upload with Express + Multer: http://ift.tt/2m48Oav, or
Switch to any cloud solution of your choice (Amazon S3, Google Drive, etc).
We will be using File upload with Hapi.js as our backend throughout this articles. We will also learn the tricks to enable fake upload on the front-end.
File Upload Component HTML
Alright, let's start creating our Angular file upload component.
<!-- page-file-upload.component.html --> <div> <!--UPLOAD--> <form #f="ngForm" enctype="multipart/form-data" novalidate *ngIf="currentStatus === STATUS_INITIAL || currentStatus === STATUS_SAVING"> <h1>Upload images</h1> <div class="dropbox"> <input type="file" multiple [name]="uploadFieldName" (change)="filesChange($event.target.name, $event.target.files)" [disabled]="currentStatus === STATUS_SAVING" accept="image/*" #photos> <p *ngIf="currentStatus === STATUS_INITIAL"> Drag your file(s) here to begin<br> or click to browse </p> <p *ngIf="currentStatus === STATUS_SAVING"> Uploading files... </p> </div> </form> </div>
Notes:-
Our upload form will have a few statuses: STATUS_INITIAL, STATUS_SAVING, STATUS_SUCCESS, STATUS_FAILED, the variable name is pretty expressive themselves.
We will display the upload form when the status is initial or saving.
The form attribute enctype="multipart/form-data" is important. To enable file upload, this attribute must be set. Learn more about enctype here.
We have a file input <input type="file" /> to accept file upload. The property multiple indicate it's allow multiple file upload. Remove it for single file upload.
We will handle the file input change event. Whenever the file input change (someone drop or select files), we will trigger the filesChange function and pass in the control name and selected files $event.target.files, and then upload to server.
We limit the file input to accept images only with the attribute accept="image/*".
The file input will be disabled during upload, so user can only drop / select files again after upload complete.
We set a template variable #photos to the file input. This gives us a reference to the file input control. Later, you can see we use the photos variable in displaying number of files uploading Uploading files....
Style our File Upload Component
Now, that's the interesting part. Currently, our component look like this:
We need to transform it to look like this:
Let's style it!
/* page-file-upload.component.css */ .dropbox { outline: 2px dashed grey; /* the dash box */ outline-offset: -10px; background: lightcyan; color: dimgray; padding: 10px 10px; min-height: 200px; /* minimum height */ position: relative; cursor: pointer; } .dropbox:hover { background: lightblue; /* when mouse over to the drop zone, change color */ } input[type="file"] { opacity: 0; /* invisible but it's there! */ width: 100%; height: 200px; position: absolute; cursor: pointer; } .dropbox p { font-size: 1.2em; text-align: center; padding: 50px 0; }
With only few lines of css, our component looks prettier now.
Notes:-
We make the file input invisible by applying opacity: 0 style. This doesn't hide the file input, it just make it invisible.
Then, we style the file input parent element, the dropbox css class. We make it look like a drop file zone surround with dash.
Then, we align the text inside dropbox to center.
File Upload Component Code
Let's proceed to code our component class.
// page-file-upload.component.ts import { Component } from '@angular/core'; import { FileUploadService } from './file-upload.service'; // we will create this next! @Component({ selector: 'page-file-upload', templateUrl: './page-file-upload.component.html', styleUrls: ['./page-file-upload.component.css'] }) export class PageFileUploadComponent { uploadedFiles = []; uploadError; currentStatus: number; uploadFieldName = 'photos'; readonly STATUS_INITIAL = 0; readonly STATUS_SAVING = 1; readonly STATUS_SUCCESS = 2; readonly STATUS_FAILED = 3; constructor(private _svc: FileUploadService) { this.reset(); // set initial state } filesChange(fieldName: string, fileList: FileList) { // handle file changes const formData = new FormData(); if (!fileList.length) return; // append the files to FormData Array .from(Array(fileList.length).keys()) .map(x => { formData.append(fieldName, fileList[x], fileList[x].name); }); // save it this.save(formData); } reset() { this.currentStatus = this.STATUS_INITIAL; this.uploadedFiles = []; this.uploadError = null; } save(formData: FormData) { // upload data to the server this.currentStatus = this.STATUS_SAVING; this._svc.upload(formData) .take(1) .delay(1500) // DEV ONLY: delay 1.5s to see the changes .subscribe(x => { this.uploadedFiles = [].concat(x); this.currentStatus = this.STATUS_SUCCESS; }, err => { this.uploadError = err; this.currentStatus = this.STATUS_FAILED; }) } }
Notes:-
Later on, we will call the Hapi.js file upload API to upload images, the API accept a field call photos. That's our file input field name.
We handle the file changes with the filesChange function. FileList is an object returned by the files property of the HTML element. It allow us to access the list of files selected with the element. Learn more [here]((http://ift.tt/1H81MY1).
We then create a new FormData, and append all our photos files to it. FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values. Learn more here.
The save function will call our file upload service (hang on, we will create the service next!). We also set the status according to the result.
File Upload Service
// file-upload.service.ts import { Injectable } from '@angular/core'; import { Http, RequestOptionsArgs, Headers } from '@angular/http'; @Injectable() export class FileUploadService { baseUrl = 'http://localhost:3001'; // our local Hapi Js API constructor(private _http: Http) { } upload(formData) { const url = `${this.baseUrl}/photos/upload`; return this._http.post(url, formData) .map(x => x.json()) .map((x: any[]) => x // add a new field url to be used in UI later .map(item => Object .assign({}, item, { url: `${this.baseUrl}/images/${item.id}` })) ); } }
Nothing much, the code is pretty expressive itself. We upload the files, wait for the result, map it accordingly.
Now wire up your component and service to module, usually app.module.ts, and run it.
Display Success and Failed Result
We can upload the files successfully now. However, there's no indication in UI. Let's update our HTML.
<!-- page-file-upload.component.html --> <div> <!--UPLOAD--> ... <!--SUCCESS--> <div class="margin-20" *ngIf="currentStatus === STATUS_SUCCESS"> <h2>Uploaded file(s) successfully.</h2> <p> <a href="javascript:void(0)" (click)="reset()">Upload again</a> </p> <ul class="list-unstyled"> <li *ngFor="let item of uploadedFiles"> <img [src]="item.url" class="img-responsive img-thumbnail" [alt]="item.originalName"> </li> </ul> </div> <!--FAILED--> <div class="margin-20" *ngIf="currentStatus === STATUS_FAILED"> <h2>Uploaded failed.</h2> <p> <a href="javascript:void(0)" (click)="reset()">Try again</a> </p> <pre></pre> </div> </div>
Notes:-
Display the uploaded image when upload successfully.
Display the error message when upload failed.
Fake the Upload in Front-end
If you are lazy to start the back-end application (Hapi, Express, etc) to handle file upload. Here is a fake service to replace the file upload service.
// file-upload.fake.service.ts import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @Injectable() export class FileUploadFakeService { upload(formData: any) { const photos: any[] = formData.getAll('photos'); const promises = photos.map((x: File) => this.getImage(x) .then(img => ({ id: img, originalName: x.name, fileName: x.name, url: img }))); return Observable.fromPromise(Promise.all(promises)); } private getImage(file: File) { return new Promise((resolve, reject) => { const fReader = new FileReader(); const img = document.createElement('img'); fReader.onload = () => { img.src= fReader.result; resolve(this.getBase64Image(img)); } fReader.readAsDataURL(file); }) } private getBase64Image(img) { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const dataURL = canvas.toDataURL('image/png'); return dataURL; } }
Came across this solution in this Stackoverflow post. Pretty useful. My online demo is using this service.
Basically, what the code do is read the source, draw it in canvas, and save it as data url with the canvas toDataURL function. Learn more about canvas here.
If you realize, our fake service has a same public interface as the real file upload service, both has upload function and return list of files. This is important for the next step, swap the real file upload service with the fake one.
Swap the Real File Upload with the Fake Service
First you might think that to use the fake service, you need to register the fake service in module, and import it in our file upload component like how we do usually. However, there's a quicker way, with Angular dependency injection (DI). Let's look at our App module.
// app.module.ts ... import { PageFileUploadComponent, FileUploadFakeService, FileUploadService } from './file-upload'; @NgModule({ ... providers: [ // FileUploadService, // normally we do this, comment it, we do the below instead { provide: FileUploadService, useClass: FileUploadFakeService }, // we can do this instead ], ... }) export class AppModule { }
With this, you don't need to change your component code, stop your backend API, refresh the browser, you should see our app is still working, calling fake service instead.
In short, Providers: [FileUploadService] is the short form of Providers: [{ provide: FileUploadService, useClass: FileUploadService }]. Therefore, as long as we have another class with similar interface, we can swap it easily.
Angular DI is powerful. We'll leave that for another post.
Summary
That's it. This is how you can handle file upload without using any 3rd party libraries and plugins in Angular. It isn't that hard right?
Happy coding!
The UI (Front-end)
Sourcecode: http://ift.tt/2p20lqz
Demo: http://ift.tt/2o0mYH6
The API (Back-end)
File upload with Hapi.js: http://ift.tt/2m8mJxf, or
File upload with Express + Multer: http://ift.tt/2m48Oav, or
Switch to any cloud solution of your choice (Amazon S3, Google Drive, etc).
via Scotch.io http://ift.tt/2p3NscK
0 notes
mbaljeetsingh · 8 years ago
Text
Angular File Uploads with an Express Backend
In this tutorial, I will be teaching on how to upload files in Angular 2+.
Throughout this tutorial, Angular means Angular version greater than 2.x unless stated otherwise.
In this tutorial, I will also help you all create a server script that handles the file uploads.
I will teach two methods of file uploads using Angular.
The first method entails using the ng2-file-upload package, while the second method is handling the uploads without using any third party package.
What We Will Build
This is what the app will look like when we are done building. For the server side, we will be using Node.js (Express) for the script that handles the upload.
Getting Started With Express Generator
To get started, we will need to install express generator, to take care of the configurations, and make our work much easier.
So we run this command.
sudo npm install -g express-generator
Once Express generator has been installed, its time to create our application.
So we run the following command.
express -e angular-file
After creating the application, we would need to move into the directory, and run npm install
cd angular-file npm install
At this point, if we run ng start command, we should be able to see the default express page.
Installing The Multer Library
The next step would be to install Multer. Multer is a package for handling file uploads in express js. To install Mutler, we run the following command.
npm install multer --save
At this point, we have multer installed, we would need to use it in the route handling our upload function.
Setting Up The Upload Route
Let us open up our routes\index.js and replace it with the following:
//require express library var express = require('express'); //require the express router var router = express.Router(); //require multer for the file uploads var multer = require('multer'); // set the directory for the uploads to the uploaded to var DIR = './uploads/'; //define the type of upload multer would be doing and pass in its destination, in our case, its a single file with the name photo var upload = multer({dest: DIR}).single('photo'); /* GET home page. */ router.get('/', function(req, res, next) { // render the index page, and pass data to it. res.render('index', { title: 'Express' }); }); //our file upload function. router.post('/', function (req, res, next) { var path = ''; upload(req, res, function (err) { if (err) { // An error occurred when uploading console.log(err); return res.status(422).send("an Error occured") } // No error occured. path = req.file.path; return res.send("Upload Completed for "+path); }); }) module.exports = router;
In the above route file, we imported the mutler library, Created a variable DIR, that holds the destination point, we then define an upload varaible, that holds the mutler upload function telling it that it would be uploading a single file, with the name photo.
In our post route, we call the upload function, acting as a middleware, adding a callback, so we can know if the file was uploaded or not.
Once the file has been uploaded, mutler provides an interface for us to get the location of the file that has been uploaded, using the req.file.path, which we assign to a variable, and return it as the success message.
At this point, however, if we try to access the route from any client and upload files to it, it would give a cors origin blocked error, which of cause, is right, as we are going to be calling the upload api from another domain.
However, there's a fix for that.
Creating The Cors Middleware
Locate your app.js file in the root folder, and lets locate the line that says app.use('/', routes) and just before that line, lets add the following:
//create a cors middleware app.use(function(req, res, next) { //set headers to allow cross origin request. res.header("Access-Control-Allow-Origin", "*"); res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); });
What we have done above, is to create a middleware, that adds the cors origin header ro the response, as well as allowed methods for that origin.
At this point, we can hit ctrl+c on the terminal, and then run npm start again to reload changes.
Our server Script is now ready to receive and upload files to our root upload folder.
Getting Started With The Angular Cli
Now it's time we move to create the angular project that does the file upload.
let's move into the public folder, and we would use the angular-cli to create a new angular project
So we run the below command to install the angular-cli which is a Command Line Interface for developing Angular apps.
So in our terminal, we run the following command to install it and create a new project.
//install the angular-cli npm install -g angular-cli //change directory to the public folder of our working directory cd public //create a new angular project called testupload. ng new testupload //change directory to the test upload folder cd testupload //serve the angular application ng serve
At this point, we see that npm packages are being installed, and as such, we wait till they are done.
Once they have been installed, we can run the ng serve to serve our application for us to see.
Extra Reading on Angular Cli Use the Angular CLI For Faster Angular 2 Projects
Now it's time to create the actual file upload.
Using The Ng2-File-Upload Package
Method 1. Using the ng2-file-upload package.
Now after we have served the application, we would see a screen like this when we navigate to localhost:4200. Now, lets run the following command to install the ng2-file-upload package.
//install the file upload plugin and save to composer.json file. npm i ng2-file-upload --save
This installs the module into our node-modules folder, and as well saves it into our JSON file. Now lets head over to the file in src/app/app.component.ts
We would replace the contents of the file with this one below.
//import component and the oninit method from angular core import { Component, OnInit } from '@angular/core'; //import the file uploader plugin import { FileUploader } from 'ng2-file-upload/ng2-file-upload'; //define the constant url we would be uploading to. const URL = 'http://localhost:8000/api/upload'; //create the component properties @Component({ //define the element to be selected from the html structure. selector: 'app-root', //location of our template rather than writing in-line templates. templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { //declare a property called fileuploader and assign it to an instance of a new fileUploader. //pass in the Url to be uploaded to, and pass the itemAlais, which would be the name of the //file input when sending the post request. public uploader:FileUploader = new FileUploader({url: URL, itemAlias: 'photo'}); //This is the default title property created by the angular cli. Its responsible for the app works title = 'app works!'; ngOnInit() { //override the onAfterAddingfile property of the uploader so it doesn't authenticate with //credentials. this.uploader.onAfterAddingFile = (file)=> { file.withCredentials = false; }; //overide the onCompleteItem property of the uploader so we are //able to deal with the server response. this.uploader.onCompleteItem = (item:any, response:any, status:any, headers:any) => { console.log("ImageUpload:uploaded:", item, status, response); }; } }
Here, we import component, alongside the OnInit class, so we can implement the ngOnInit function, which serve like a type of constructor for our component.
Then we import the file uploader class. We then define a constant that holds the url we are uploading to.
In our Appcomponent class, we define a public property called uploader, and assign it to an instance of the file uploader, passing along our URL and an extra itemAlais property which we would call the File Input.
The itemAlias property refers to the name we would like to call out file input.
Overiding The onAfterAddingFile Function
We then call the ngOnit function, where we override two of the uploaders function. The first function we override is the onAfterAddingFile function which is triggered after a file has been chosen, and we set the credentials to the file to be false. i.e we are not authenticating with credentials.
The next function we override is the onCompleteItem function. The reason we override this is so we can get the response from the server.
In our case, we just console log the status, response and the item.
Now we move into our html file and replace it with the following content, so we can add the input type.
<h1> <!-- here we echo the title from the component --> </h1> <!-- File input for the file-upload plugin, with special ng2-file-upload directive called ng2FileSelect --> <input type="file" name="photo" ng2FileSelect [uploader]="uploader" /> <!-- button to trigger the file upload when submitted --> <button type="button" class="btn btn-success btn-s" (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length"> Upload with ng-2 file uploader </button>
So we create an input of type file and we attach the ng2FileSelect directive to it, which makes it possible to bind the uploader attribute which it provide to our own uploader.
Then we create a button that is disabled if there is no item in the upload queue and has a click function to upload all files in the queue.
However, if we save our file at this time and run it, we would run into errors.
Including The Ng2FileSelect Directive
We would have to add a declaration to our app.module.ts, so we can use the ng2FileSelect directive.
So we add this line to the top of our app.module.ts
//import the ng2-file-upload directive so we can add it to our declarations. import { FileSelectDirective } from 'ng2-file-upload';
And we also add the FileSelectDirective to our declarations
declarations: [ AppComponent, FileSelectDirective ],
So our app.module.ts should look this way.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { FileSelectDirective } from 'ng2-file-upload'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent, FileSelectDirective ], imports: [ BrowserModule, FormsModule, HttpModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Now if we launch our application and upload, we should see that our file is sent to the server.
So on the Root folder, if we go to uploads folder, we should see that our images are being uploaded. Viola we just uploaded files using angular.
Using The Underlying Form Data
However, there is a second method if we do not want to use the above plugin which requires using the underlying form-data to create the form data.
Let us create an extra file input and button in our app.component.html,
So our html structure looks this way.
<h1> <!-- here we echo the title from the component --> </h1> <!-- File input for the file-upload plugin, with special ng2-file-upload directive called ng2FileSelect --> <input type="file" name="photo" ng2FileSelect [uploader]="uploader" /> <!-- button to trigger the file upload when submitted --> <button type="button" class="btn btn-success btn-s" (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length"> Upload with ng-2 file uploader </button> <!-- File input for upload without using the plugin. --> <input id="photo" type="file" /> <!-- button to trigger the file upload when submitted --> <button type="button" class="btn btn-success btn-s" (click)="upload()"> Upload with method 2 </button>
Note that i have added another file input with an id of photo, and another button which has a click event to upload.
Now let's create the upload function that handles the file upload.
Copy and replace your app component.ts with this.
//import component, ElementRef, input and the oninit method from angular core import { Component, OnInit, ElementRef, Input } from '@angular/core'; //import the file-upload plugin import { FileUploader } from 'ng2-file-upload/ng2-file-upload'; //import the native angular http and respone libraries import { Http, Response } from '@angular/http'; //import the do function to be used with the http library. import "rxjs/add/operator/do"; //import the map function to be used with the http library import "rxjs/add/operator/map"; const URL = 'http://localhost:8000/api/upload'; //create the component properties @Component({ //define the element to be selected from the html structure. selector: 'app-root', //location of our template rather than writing inline templates. templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { //declare a property called fileuploader and assign it to an instance of a new fileUploader. //pass in the Url to be uploaded to, and pass the itemAlais, which would be the name of the //file input when sending the post request. public uploader:FileUploader = new FileUploader({url: URL, itemAlias: 'photo'}); //This is the default title property created by the angular cli. Its responsible for the app works title = 'app works!'; ngOnInit() { //override the onAfterAddingfile property of the uploader so it doesn't authenticate with //credentials. this.uploader.onAfterAddingFile = (file)=> { file.withCredentials = false; }; //overide the onCompleteItem property of the uploader so we are //able to deal with the server response. this.uploader.onCompleteItem = (item:any, response:any, status:any, headers:any) => { console.log("ImageUpload:uploaded:", item, status, response); }; } //declare a constroctur, so we can pass in some properties to the class, which can be //accessed using the this variable constructor(private http: Http, private el: ElementRef) { } //the function which handles the file upload without using a plugin. upload() { //locate the file element meant for the file upload. let inputEl: HTMLInputElement = this.el.nativeElement.querySelector('#photo'); //get the total amount of files attached to the file input. let fileCount: number = inputEl.files.length; //create a new fromdata instance let formData = new FormData(); //check if the filecount is greater than zero, to be sure a file was selected. if (fileCount > 0) { // a file was selected //append the key name 'photo' with the first file in the element formData.append('photo', inputEl.files.item(0)); //call the angular http method this.http //post the form data to the url defined above and map the response. Then subscribe //to initiate the post. if you don't subscribe, angular wont post. .post(URL, formData).map((res:Response) => res.json()).subscribe( //map the success function and alert the response (success) => { alert(success._body); }, (error) => alert(error)) } } }
What has changed?
In this updated version, i have imported element ref and input from angular core.
I also imported the HTTP and response from the angular HTTP library.
I also went ahead to import the map and do RX's functions to be used with our HTTP class.
In the app component class, two things were added. 1.) A constructor 2.) The upload function.
In the constructor, we pass in our HTTP and element ref instances, so they can be accessed by this.http and this.el.
The upload function here is where the work lies.
We declare inputel, which is of type htmlinputelement, and we set it to the instance of the file input we created with an id of photo using the nativelement.queryselector of the el.
We then declare a variable filecount of type number and set it to the length of the files in the inputelement.
We then use an if statement to be sure that a file was selected.
We then loop through the file and we append the first element of the file as the value of the key 'photo' which our server expects and then append to our formdata.
We then call our http library to post to our previously defined url, sending the formData as params.
At this point, if we try out our app and check the uploads folder, we should also see that the files are being uploaded.
Conclusion
If you completed the above tutorial successfully, you have learned how to upload a file in Angular.
We have seen two different methods of uploading files. For those who do not like using thirld party libraries, we have used the underlying form data, and for those who do not mind to use plugins, we have used the ng2-file-upload plugin by valor.
via Scotch.io http://ift.tt/2lz3A3U
0 notes