Sunday, 14 May 2017

Changing the Image in folder does not reflect changes in browser

What I am doing:

I am displaying an image from image file using mongodb database, nodejs and angular 4. The image is displayed well.

Problem:

The path of the image that I am showing is assets/Images/1.jpg. This path comes from database.

Now, I go to the folder assets/Images and delete 1.jpg. Then I upload a new Image to this folder and then I rename that newly uploaded image to 1.jpg.

But the browser is still displaying old image.

What I tried:

I tried to refresh the browser tab. But still it is displaying the old image.

So, as mentioned in this post, I tried empty cache and hard reload. Still the browser is displaying the old Image.

Then I restarted the server. And as expected browser shows me the new Image.

What I want:

I want that the browser should immediately show me the changed Image as soon as I copy-paste new Image, delete the old one and rename the new image.

If a sudden change is not possible, then I want the browser to display new Image when page is refreshed programatically.

Sample that reproduces issue

You can download the sample at this link: https://drive.google.com/file/d/0B5WyqSALui0bekJhTGtudWNOd28/view?usp=sharing

How to use sample to run the app

  1. Download the sample files from above mentioned link.
  2. Extract the file.
  3. open git bash or terminal and navigate to image-sample folder
  4. run npm install command.
  5. After dependencies are installed, cd node-files, then cd seed
  6. run node image-seeder command. This will create a sample database in mongodb and then insert sample data.
  7. run cd .. twice to come to the original folder and then
  8. run the application by npm start
  9. open browser and type localhost://3000 in the address bar. You should see the image.
  10. Now browse src/assets/Images folder
  11. Delete 1.jpg
  12. Rename 2.jpg to 1.jpg and then look at the browser. The image will not be changed.
  13. Refresh the browser window. Still Old Image will be shown.
  14. Restart the server and then you can see the changed Image.

Code and Description

First I created the angular app using

ng new image-sample

Then I added some dependencies in package.json as follows:

"body-parser": "^1.17.1",
"cookie-parser": "^1.4.3",
"core-js": "^2.4.1",
"express": "^4.15.2",
"fs": "0.0.1-security",
"mongoose": "^4.9.9",
"morgan": "^1.8.1",
"path": "^0.12.7"

I also changed the scripts as follows:

"scripts": {
    "ng": "ng",
    "start": "npm run serve-build",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "build:nodeserver": "ng build && cp nodeserver/* dist",
    "build:nodeserver-prod": "ng build -prod && cp nodeserver/* dist",
    "serve-build": "npm run build:nodeserver && cd dist && node server.js",
    "serve-build-prod": "npm run build:nodeserver-prod && cd dist && node server.js"
},

Then I install these dependencies using:

npm install

Then I created some folders and files in image-sample directory as follows:

nodeserver
   |-- server.js

node-files
   |--models
       |-- db.js
       |-- Image.js
   |--api
       |-- image.js
   |--seed
       |-- image-seeder.js

Here is the content of above mentioned files:

nodeserver/server.js

var express = require('express');
var morgan = require('morgan');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var path = require('path');
var fs = require('fs');

require('../node-files/models/db');

var image = require('../node-files/api/image');

var app = express();
var staticRoot = __dirname + '/';

app.set('port', (process.env.PORT || 3000));

app.use(express.static(staticRoot));

app.use(morgan('dev'));
app.use(bodyParser.json());
app.use(cookieParser());

app.use(function(req, res, next){

    // if the request is not html then move along
    var accept = req.accepts('html', 'json', 'xml');
    if(accept !== 'html'){
        return next();
    }

    // if the request has a '.' assume that it's for a file, move along
    var ext = path.extname(req.path);
    if (ext !== ''){
        return next();
    }

    fs.createReadStream(staticRoot + 'index.html').pipe(res);

});

app.use('/api/image', image);

app.listen(app.get('port'), function() {
    console.log('app running on port', app.get('port'));
});

node-files/models/db.js

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/sampleimagedb')

node-files/models/Image.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var imageSchema = new Schema({
    url: {type: String, required: true},
    width: {type: Number, required: true},
    height: {type: Number, required: true}
});

var Image = module.exports = mongoose.model('Image', imageSchema);

module.exports.getAllImages = function(callback){
    Image.find().lean().exec(function(err, images){
        if (err) return callback(err, null);
        callback(null, images);
    });
};

node-files/api/image.js

var express = require('express')
var router = express.Router();

var Image = require('../models/Image');

router.get('/', function(req, res, next) {

    Image.getAllImages(function(err, images) {
        if(err) { return res.status(400).json(err); }
        else { res.status(200).json(images); }
    });

});

module.exports = router;

node-files/seed/image-seeder.js

var Image = require('../models/Image');

var mongoose = require('mongoose');
mongoose.connect('localhost:27017/sampleimagedb');

var images = [
    new Image({
        url: 'assets/Images/1.jpg',
        width: 300,
        height: 200
    })
];

var done = 0;

for(var i = 0; i < images.length; i++)
{
    images[i].save(function(err, result){
        done++;
        if(done == images.length){
            exit();
        }
    });
}

function exit() {
    mongoose.disconnect();
    console.log(images.length + ' Image(s) added to the database');
}

And then on angular side, I created a folder called models under src/app. In which I added Image.ts file which looks like:

export class Image
{
    url: string;
    height: number;
    width: number;

    constructor(url: string, height: number, width: number) {
      this.url = url;
      this.height = height;
      this.width = width;
    }
}

I created a services folder inside src/app folder. Then I added a service called ImageService in services folder using angular-cli tool as follows:

ng g service image

That generates two files, one of them is image.service.ts which looks like:

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';

import { Image } from '../app.component';

@Injectable()
export class ImageService {

  constructor(private http:Http) { }

  public image: Image

  getImages(): Observable<Image[]> {
    return this.http.get('/api/image')
                    .map(res => res.json());
  }

}

Then I added this service in the providers section of app.module.ts which looks like:

providers: [ImageService],

Then in the app.component.ts file I call the ImageService as follows:

import { Component, OnInit } from '@angular/core';
import { ImageService } from './services/image.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{

    images: Image[];

    ngOnInit() {
        this.imageService.getImages()
            .subscribe(
                response => this.images = response,
                error => console.log(error)
            );;
    }

    constructor(private imageService: ImageService) {

    }

}

And finally in app.component.html:

<div *ngFor="let image of images">
    <img [src]="image.url" style="left:0px;top:0px;" 
         [style.width.px]="image.width" [style.height.px]="image.height"/>
</div>



via Vishal

No comments:

Post a Comment