Home > front end >  GET http://localhost:4200/tour-x-b-cover.jpeg 404 (Not Found) - Broken Image Path(Not Shown)
GET http://localhost:4200/tour-x-b-cover.jpeg 404 (Not Found) - Broken Image Path(Not Shown)

Time:01-29

My table list of tours displays all the required info except that of image Cover. I cannot figure out why the image path is broken. I suspected my back-end code that serves my static files but as far as I can tell all appears to be fine.

In back-end code I have the following line of code for serving static files on my app.js file.

app.use(
  '/public/img/countries',
  express.static(`${__dirname}/public/img/countries`)
);
app.use('/public/img/tours', express.static(`${__dirname}/public/img/tours`));
app.use('/public/img/users', express.static(`${__dirname}/public/img/users`));

On my tour handler function, tourController.js back-end code I have the below code as it specifically pertains to images.

const multerStorage = multer.memoryStorage();


const multerFilter = (req, file, cb) => {
  if (file.mimetype.startsWith('image')) {
    cb(null, true);
  } else {
    cb(new AppError('Not an image! Please upload only images.', 400), false);
  }
};

const upload = multer({
  storage: multerStorage,
  fileFilter: multerFilter
  // limits: { fileSize: maxSize }
});

exports.uploadTourImages = upload.fields([
  //req.files for fields
  { name: 'imageCover', maxCount: 1 },
  { name: 'images', maxCount: 3 }
]);


exports.resizeTourImages = catchAsync(async (req, res, next) => {
  if (!req.files.imageCover || !req.files.images) return next();

  // 1) Cover image
  req.body.imageCover = `tour-${req.params.id}-${Date.now()}-cover.jpeg`;

  await sharp(req.files.imageCover[0].buffer)
    .rotate()
    .resize(1903, 1268)
    .toFormat('jpeg')
    .jpeg({ quality: 90 })
    .toFile(`public/img/tours/${req.body.imageCover}`);

  req.body.images = [];

  await Promise.all(
    req.files.images.map(async (file, i) => {
    
      const filename = `tour-${req.params.id}-${Date.now()}-${i   1}.jpeg`;

      await sharp(file.buffer)
        .rotate()
        .resize(1903, 1268)
        .toFormat('jpeg')
        .jpeg({ quality: 90 }) 
        .toFile(`public/img/tours/${filename}`);

      req.body.images.push(filename);
    })
  );

  next();
});

On my factory function on my back-end model

exports.getAll = Model =>
  catchAsync(async (req, res, next) => {
    let filter = {};
    if (req.params.tourId) filter = { tour: req.params.tourId };

    //EXECUTE QUERY
    const features = new APIFeatures(Model.find(filter), req.query)
      .filter()
      .sort()
      .limitFields()
      .paginate();

    const doc = await features.query;

    //SEND REPONSE
    res.status(200).json({
      status: 'success',
      results: doc.length,
      data: {
        data: doc
      }
    });

    next();
  });

On my Front-end, I have the below code for my table on my tour-list.component.html

<p-table [value]="tours" styleClass="p-datatable-gridlines" responsiveLayout="scroll">
          <ng-template pTemplate="header">
            <tr>
              <th pSortableColumn="name">Name<p-sortIcon field="name"></p-sortIcon></th>
              <th>Image Cover</th>
              <th pSortableColumn="price">Price<p-sortIcon field="price"></p-sortIcon></th>
              <th pSortableColumn="ratingsAverage">
                Ratings Average <p-sortIcon field="ratingsAverage"></p-sortIcon>
              </th>
              <th pSortableColumn="country">Country<p-sortIcon field="country"></p-sortIcon></th>
              <th pSortableColumn="difficulty">
                Difficulty<p-sortIcon field="difficulty"></p-sortIcon>
              </th>
              <th pSortableColumn="availabilityDates">
                Availability Dates<p-sortIcon field="availabilityDates"></p-sortIcon>
              </th>
              <th pSortableColumn="maxGroupPair">
                Maximum Group Pairs<p-sortIcon field="maxGroupPair"></p-sortIcon>
              </th>
              <th pSortableColumn="minimumAge">
                Minimum Age<p-sortIcon field="minimumAge"></p-sortIcon>
              </th>
              <th pSortableColumn="dateCreated">
                Date Created<p-sortIcon field="dateCreated"></p-sortIcon>
              </th>
              <th></th>
            </tr>
          </ng-template>
          <ng-template pTemplate="body" let-tour>
            <tr>
              <td>{{ tour.name }}</td>
              <td><img [src]="tour.imageCover" width="100px" height="67px" alt="" /></td>
              <td>{{ tour.price }}</td>
              <td>{{ tour.ratingsAverage }}</td>
              <td>{{ tour.country.name }}</td>
              <td>{{ tour.difficulty }}</td>
              <td>{{ tour.availabilityDates }}</td>
              <td>{{ tour.maxGroupPair }}</td>
              <td>{{ tour.minimumAge }}</td>
              <td>{{ tour.dateCreated | date: 'longDate' }}</td>
            </tr>
          </ng-template>
        </p-table>

My tours-list.component.ts file has the below code

import { Component, OnInit } from '@angular/core';
import { ToursService } from '@tourafrika/tours';

@Component({
  selector: 'admin-tours-list',
  templateUrl: './tours-list.component.html',
  styles: []
})
export class ToursListComponent implements OnInit {
  tours = [];

  constructor(private tourService: ToursService) {}

  ngOnInit(): void {
    this._getTours();
  }

  private _getTours() {
    this.tourService.getTours().subscribe((tours) => {
      this.tours = tours;
    });
  }
}

My Library model code on my front-end has the below code.

import { Country } from './country';
import { User } from './user';

export class Tour {
  id?: string;
  name?: string;
  slug?: string;
  country?: Country;
  duration?: number;
  maxGroupPair?: number;
  singleSupplement?: number;
  difficulty?: string;
  ratingsAverage?: number;
  ratingsQuantity?: number;
  price?: number;
  priceDiscount?: number;
  overview?: string;
  imageCover?: string;
  images?: string[];
  dateCreated?: string;
  availabilityDates?: string[];
  isFeatured?: boolean;
  secretTour?: string;
  minimumAge?: string;
  departureLocation?: string;
  locations?: string;
  guides?: [User];
}

export interface ITourListResponse {
  status: string;
  results: number;
  data: { data: Tour[] };
}

My services file of tours.service.ts has the below code.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Tour, ITourListResponse } from '../models/tour';
import { map } from 'rxjs/operators';
import { environment } from '@env/environment';

@Injectable({
  providedIn: 'root'
})
export class ToursService {
  apiURLTours = environment.apiUrl   'tours';
  constructor(private http: HttpClient) {}

  getTours(): Observable<Tour[]> {
    return this.http
      .get<ITourListResponse>(this.apiURLTours)
      .pipe(map((response: ITourListResponse) => response.data.data));
  }

Thanks!

CodePudding user response:

I have solved this by completely restructuring my code on my toursController.ts file on my backend by running the below code to redefine my imageCover path. All good now!

 const { file } = req;
  let imagepath;

  if (file) {
    const fileName = req.file.filename;
    const basePath = `${req.protocol}://${req.get('host')}/public/img/tours/`;
    imagepath = `${basePath}${fileName}`;
  } else {
    imagepath = tour.imageCover;
  }
  •  Tags:  
  • Related