0

I want to use formidable and knox to upload files to AWS S3 bucket, but I get a 400 each time and the file is not uploaded. My code is similar to this: nodejs knox put to s3 results in a 403, the only difference is that the first argument of my readFile is from my windows temp folder, I tried the solution in the comment by ensuring my bucket name is only small letters but this also did not work. Please help if you can, thank you. My codes are below:

App.js

const express = require("express"),
  path = require("path"),
  config = require("./config"),
  knox = require("knox"),
  fs = require("fs"),
  os = require("os"),
  formidable = require("formidable"),
  gm = require("gm"),
  mongoose = require("mongoose");

mongoose.connect(config.dbURL, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

const app = express();

app.set("views", path.join(__dirname, "views"));
app.engine("html", require("hogan-express"));
app.set("view engine", "html");

app.use(express.static(path.join(__dirname, "public")));
app.set("port", process.env.PORT || 3000);
app.set("host", config.host);

let knoxClient = knox.createClient({
  key: config.S3AccessKey,
  secret: config.S3Secret,
  bucket: config.S3Bucket,
});

const server = require("http").createServer(app);
const io = require("socket.io")(server);

require("./routers")(
  express,
  app,
  formidable,
  fs,
  os,
  gm,
  knoxClient,
  mongoose,
  io
);

server.listen(app.get("port"), function () {
  console.log("PhotoGRID Running on port: " + app.get("port"));
});

Router(index.js)

module.exports = (
  express,
  app,
  formidable,
  fs,
  os,
  gm,
  knoxClient,
  mongoose,
  io
) => {
  //os.tmpDir = os.tmpdir;
  let Socket;

  io.on("connection", function (socket) {
    Socket = socket;
  });

  const singleImage = new mongoose.Schema({
    filename: {
      type: String,
      require: true,
    },
    votes: {
      type: Number,
      require: true,
    },
  });

  let singleImageModel = mongoose.model("singleImage", singleImage);
  let router = express.Router();

  router.get("/", function (req, res, next) {
    res.render("index", { host: app.get("host") });
  });

  router.post("/upload", function (req, res, next) {
    // File upload

    function generateFilename(filename) {
      let ext_regex = /(?:\.([^.]+))?$/;
      let ext = ext_regex.exec(filename)[1];
      let date = new Date().getTime();
      let charBank = "abcdefghijklmnopqrstuvwxyz";
      let fstring = "";
      for (let i = 0; i < 15; i++) {
        fstring += charBank[parseInt(Math.random() * 26)];
      }
      return (fstring += date + "." + ext);
    }

    let tmpFile, nFile, fName;
    let newForm = new formidable.IncomingForm();
    newForm.keepExtensions = true;
    newForm.parse(req, function (err, fields, files) {
      tmpFile = files.upload.path;
      fName = generateFilename(files.upload.name);
      nFile = os.tmpDir() + "/" + fName;
      res.writeHead(200, { "Content-type": "text/plain" });
      res.end();
    });

    newForm.on("end", function () {
      fs.rename(tmpFile, nFile, function () {
        // Resize the image and upload this file into the S3 bucket
        gm(nFile)
          .resize(300)
          .write(nFile, function () {
            // Upload to the S3 Bucket
            fs.readFile(nFile, function (err, buf) {
              let req = knoxClient.put(fName, {
                "Content-Length": buf.length,
                "Content-Type": "image/jpg",
              });

              req.on("response", function (res) {
                if (res.statusCode == 200) {
                  //This means that the file is in the S3 Bucket
                  console.log(fName + " is in the S3 bucket");

                  let newImage = new singleImageModel({
                    filename: fName,
                    votes: 0,
                  }).save();

                  Socket.emit("status", { msg: "Saved!!", delay: 3000 });
                  Socket.emit("doUpdate", {});

                  // Delete the Local file
                  fs.unlink(nFile, function () {
                    console.log("Local file deleted!");
                  });
                } else {
                  console.log(
                    err +
                      fName +
                      " did not get to the database or S3 bucket so " +
                      nFile +
                      " was not deleted " +
                      res.statusCode
                  );
                }
              });

              req.end(buf);
            });
          });
      });
    });
  });

  app.use("/", router);
};

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>PhotoGrid</title>
    <link rel="stylesheet" href="../css/main.css" />
    <script src="http://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script src="../js/photogrid_utils.js"></script>
    <script>
      $(function () {
        let host = "{{host}}";

        $(document).on("click", "#doUpload", function () {
          uploadNow();
        });

        let socket = io(host);

        socket.on("status", function (data) {
          showStatus(data.msg, data.delay);
        });

        socket.on("doUpdate", function () {
          renderList();
        });

        function uploadNow() {
          let uploadURL = host + "/upload";
          let uploadFile = $(".uploadPic");
          if (uploadFile.val() != "") {
            let form = new FormData();
            form.append("upload", uploadFile[0].files[0]);
            // Perform the AJAX POST request and send the file
            ajax({
              method: "post",
              url: uploadURL,
              success: function () {
                $(".progress").fadeOut(200);
                uploadFile.val("");
              },
              progress: function (e) {
                if (e.lengthComputable) {
                  let perc = Math.round((e.loaded * 100) / e.total);
                  $(".progress").css("width", perc + "%");
                }
              },
              payload: form,
            });
          }
        }
      });
    </script>
  </head>
  <body>
    <div class="container">
      <div class="topDeck">
        <div class="logo">
          <a href="{{host}}">
            <h1>PhotoGRID</h1>
          </a>
        </div>
        <div class="controls">
          <input type="file" name="uploadPic" class="uploadPic" />
          <button id="doUpload">Upload</button>
          <div class="progressBarDiv">
            <div class="progress"></div>
          </div>
          <h5 class="status"></h5>
        </div>
      </div>
      <div class="gallery">
        <ul>
          <!-- Repeat the following <li> structure for every image -->
          <li>
            <div class="overlay">
              <div class="voteCtrl">
                <a href="#" class="voteUp">
                  <img
                    src="../images/voteup.png"
                    alt="Click Here to Vote Up !"
                  />
                  <h4>100</h4>
                </a>
              </div>
            </div>
            <div class="imageHolder">
              <img src="../images/someimage.jpg" alt="" />
            </div>
          </li>
          <!-- End Repeat -->
        </ul>
      </div>
    </div>
  </body>
</html>

Zia
  • 307
  • 4
  • 11
  • Reconcile your current implementation/scenario with possible causes listed here: https://aws.amazon.com/premiumsupport/knowledge-center/s3-403-forbidden-error/ – Alex Dec 09 '20 at 22:42
  • @xandercoded I have gone through all the possible causes and I don't think my issue is caused by any. Can you think of any other reason why my files are not uploaded? – Zia Dec 09 '20 at 22:53
  • Could be failing for various reasons. What's the response message? Compare the `400` response code (or `403`) with what's listed here https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList. – Alex Dec 09 '20 at 23:04
  • @xandercoded I opened the link and I saw so many 400 status codes with their error names. What I am writing to the console is just the status code, I sincerely don't know how to write the error name to the console. If only I can it might just help me know the exact 400 error I am dealing with – Zia Dec 09 '20 at 23:49

1 Answers1

0

I couldn't get my code to work with knox, I kept getting a 400 statusCode and my files were not being uploaded to S3. So I used aws-sdk and it worked like magic. My files are being uploaded and saved to my mongo database. For those who might bump into the same problem, this is what I did:

const { config } = require("aws-sdk");

module.exports = (express, app, formidable, fs, os, gm, s3, mongoose, io) => {
  //os.tmpDir = os.tmpdir;
  let Socket;

  io.on("connection", function (socket) {
    Socket = socket;
  });

  const singleImage = new mongoose.Schema({
    filename: {
      type: String,
      require: true,
    },
    votes: {
      type: Number,
      require: true,
    },
  });

  let singleImageModel = mongoose.model("singleImage", singleImage);
  let router = express.Router();

  router.get("/", function (req, res, next) {
    res.render("index", { host: app.get("host") });
  });

  router.post("/upload", function (req, res, next) {
    // File upload

    function generateFilename(filename) {
      let ext_regex = /(?:\.([^.]+))?$/;
      let ext = ext_regex.exec(filename)[1];
      let date = new Date().getTime();
      let charBank = "abcdefghijklmnopqrstuvwxyz";
      let fstring = "";
      for (let i = 0; i < 15; i++) {
        fstring += charBank[parseInt(Math.random() * 26)];
      }
      return (fstring += date + "." + ext);
    }

    let tmpFile, nFile, fName;
    let newForm = new formidable.IncomingForm();
    newForm.keepExtensions = true;
    newForm.parse(req, function (err, fields, files) {
      tmpFile = files.upload.path;
      fName = generateFilename(files.upload.name);
      nFile = os.tmpDir() + "\\" + fName;
      res.writeHead(200, { "Content-type": "image/JPG" });
      res.end();
    });

    newForm.on("end", function () {
      fs.rename(tmpFile, nFile, function () {
        // Resize the image and upload this file into the S3 bucket
        gm(nFile)
          .resize(300)
          .write(nFile, function () {
            // Upload to the S3 Bucket
            /* fs.readFile(nFile, function (err, buf) {
              let req = knoxClient.put(fName, {
                "Content-Length": buf.length,
                "Content-Type": "image/JPG",
                "x-amz-acl": "public-read",
              }); */

            const fileContent = fs.readFileSync(nFile);

            const params = {
              Bucket: require("../config").S3Bucket,
              Key: fName,
              Body: fileContent,
            };

            s3.upload(params, function (err, data) {
              // Delete the Local file
              fs.unlink(nFile, function (err) {
                console.log("Local file deleted!");
                if (err) {
                  console.error(err);
                }
              });
              console.log("PRINT FILE: ", fName);
              if (err) {
                console.log("ERROR MSG: ", err);
                res.status(500).send(err);
              } else {
                //This means that the file is in the S3 Bucket
                console.log(fName + " is in the S3 bucket");

                let newImage = new singleImageModel({
                  filename: fName,
                  votes: 0,
                }).save();

                Socket.emit("status", { msg: "Saved!!", delay: 3000 });
                Socket.emit("doUpdate", {});

                res.status(200).end();
              }
            });

            /*               req.on("response", function (res) {
                if (res.statusCode == 200) {
                  

                } else {
                  console.log(
                    err +
                      fName +
                      " did not get to the database or S3 bucket so " +
                      nFile +
                      " was not deleted " +
                      res.statusCode
                  );
                  console.log(res.statusMessage);
                }
              }); */

            // req.end(buf);
            //     });
          });
      });
    });
  });

  app.use("/", router);
};

Zia
  • 307
  • 4
  • 11