Search code examples
node.jshttpe-commerce

Nodejs serve multiple files and JSON data


I'm implementing an e-commerce website using Nodejs and Angular. I have some products, each of them has multiple picture and some info (price, name and so on).

At the moment, in my MongoDB database, I store the absolute path of the images and the following is my current API to get single product.

router.get("/:productId", async (req, res, next) => {

  const result = await product_service.product_get(req.params.productId); 
  //'result' contains the product object
  //example: {price: 10, name:"test", images["D:/img1.jpg", "D:/img2.jpg"]}
  
  if (!result) { // err object is null, report error
          console.log(err)
          root_controller.req_fail(res, err.message)
      } else {
          // instead of absolute path provide the image path 
          // so that frontend can directly request it 
          for (var filePath of product.images) {
              if(fs.existsSync(filePath)) {
                  res.send(result); // here i need to append somehow the pictures to the response as well as the result object
              }
              else {
                  root_controller.req_fail(res, "File not found");
              } 
          }   
      }
      next();
  });

How is it suppose to be managed this scenario?

Thank you in advance!


Solution

  • The first one is I get the error: Cannot set headers after they are sent to the client.

    res.writeHead(200, {
      "Content-Type": "application/octet-stream",
      "Content-Disposition": "attachment; filename=" + path.basename(filePath)
    }); // First here
    fs.createReadStream(filePath).pipe(res); // second streaming to res object
    

    This is because you are sending response twice.

    The second is that the image is in the body of my response and I lose all the other info (price, name etc...)

    From the code I can't see that you are adding up information to the res object. In this code also, you are sending res twice first in else block if you don't find the file and other one will be executed whether or not if else runs. You should do something like this:

    router.get("/:productId", async (req, res, next) => {
    
      const result = await product_service.product_get(req.params.productId) 
      
      if (!result) { // err object is null, report error
              console.log(err)
              root_controller.req_fail(res, err.message)
          } else {
              // instead of absolute path provide the image path 
              // so that frontend can directly request it 
              for (var filePath of product.images) {
                  if(fs.existsSync(filePath)) {
                      // here i need to append somehow the pictures to the response
                      // add URL in the response object with other details 
                      // create a model that can be helpful
                      const product = new Product(product.price,product.info and so on );
                      res.send(product);
                  }
                  else {
                      root_controller.req_fail(res, "File not found");
                  } 
              }   
          }
          next();
      });
    

    Just a suggestion: Instead of saving the images on the server, use AWS S3 bucket or Azure blob and just provide the URLs in the response object for a specific product. Even if you want to store the images on server provide the URL to the frontend so it can request the resource.