1

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>
Community
  • 1
  • 1
Vishal
  • 6,238
  • 10
  • 82
  • 158
  • Are you on cloudfire or anything similar which might be caching? – webbm May 14 '17 at 22:32
  • @webbm Nope. I am using localhost. – Vishal May 14 '17 at 22:34
  • 1
    OK I've read it all through, and though I don't use mongoose myself, am I correct that it is loading the image into RAM? It looks like you need to reload the image into RAM after you do your filesystem changes. – webbm May 14 '17 at 22:38
  • @webbm I am not that much advanced level programmer. But I should say that mongoose just provides path of image to the angular app. And using that path angular fetches image from the specified folder in path. As I told you I am not an advanced level programmer, I don't know how to reload the image into the RAM. Can you tell me how to do that? – Vishal May 14 '17 at 22:41
  • Could you try to restart the server after that you did the changes? Because when you use express.static, the files are cached on the server, I'm looking some things to clean that. Could you check if works? – Willian Sabião May 14 '17 at 23:08
  • Am I way off to think this is just normal browser cache and you just need to `bust` it with something like - `"assets/Images/1.jpg?cache=" + new Date().getTime();` – Dylan May 15 '17 at 00:37

1 Answers1

1

your problem is other.

You are running the ng builder and you set the output to /dist, then your static files are copied to dist folder, then you have to change the images in: image-sample/dist/assets/images. Try to do your test on this folder and it will works.

Your problem is about the build, you must watch the files changed on /src and have to run the build task again after the change, or you can change the files on dist, but I don't recommend that because when the build is run again, the file on dist is overwritten.

So you have to better your task runners and the process of the build to better your application to work how you want.

Willian Sabião
  • 134
  • 1
  • 6
  • I have tested it. You are right. When I changed the image in dist/assets/images and refresh the browser window, the image changes. – Vishal May 15 '17 at 04:25
  • First things first. I don't know configuring scripts section in package.json file. The build configurations are copied from this site: http://tattoocoder.com/angular2-giving-your-cli-server/ – Vishal May 15 '17 at 04:31
  • I don't want to copy everything to dist folder (if-possible). Because I don't like the idea of restarting the server whenever an image is uploaded. As you mentioned that if I change images inside dist/assets/Images, then whenever server restarts, the contents of dist is overwritten. So, I have to make changes to assets/Images and then restart the server. So, I am forced to restart my server, which I don't like. Is there any other way? – Vishal May 15 '17 at 04:37
  • I use the gulp to do the tasks of my projects, normally I use the gulp.watch to see the files changed on src folder and copy this files to dist folder. I'm looking about of watcher of ng builder, but it's new for me. – Willian Sabião May 15 '17 at 04:44
  • Thank you for taking time fort me. let me tell you one thing. My scripts section inside package.json has got some problem. If I change my code in anyway, then I had to manually restart the server. Before adding support of node to angular4 application, the server restarted automatically when I changed my code. Can you please check that as well? – Vishal May 15 '17 at 04:51
  • Do you have the link to documentation to ng builder? – Willian Sabião May 15 '17 at 04:56
  • Sorry, I dont have a link. – Vishal May 15 '17 at 04:58
  • I recommend that you use gulp-watch to run the build always that your files are changed, here have a text about that: https://scotch.io/tutorials/automate-your-tasks-easily-with-gulp-js Or you need to know about the equivalent on ng builder, but I believe that's hard, I research a lot about that. – Willian Sabião May 15 '17 at 05:20
  • Thanks for mentioning that. I will try to do that. If I have any problems then I will ask you. – Vishal May 15 '17 at 05:23