Image upload from Vue/Express to Spaces

July 3, 2018 195 views
Development

I'm trying to upload a file from Vue/Express app to my DigitalOcean space, but I am having issues.

I worked through this tutorial, but in this example they are just using Express and I can't submit the form data the way it is done in this tutorial.
https://www.digitalocean.com/community/tutorials/how-to-upload-a-file-to-object-storage-with-node-js

So, I continued playing around with it.

Here is the Vue part:

<template>
  <div>
    <h1>Upload a file</h1>

    <label>File
        <input type="file" id="file" ref="file" name="file" v-on:change="handleFileUpload()"/>
      </label>
        <button v-on:click="submitFile()">Submit</button>
</div>

</template>


<script>
import axios from "axios";
export default {
  data() {
    return {
      file: ""
    };
  },

  methods: {
    submitFile() {
      // Initialize the form data
      let formData = new FormData();
      // Add the form data we need to submit
      formData.append("file", this.file);
      // Make the request to the POST /single-file URL
      axios
        .post("/upload", file, {
          baseURL: "http://localhost:8000/"
          // headers: {
          //   "Content-Type": "multipart/form-data"
          // }
        })
        .then(function() {
          console.log("SUCCESS!!");
        })
        .catch(function() {
          console.log("FAILURE!!");
        });
    },
    // Handles a change on the file upload
    handleFileUpload() {
      this.file = this.$refs.file.files[0];
    }
  }
};
</script>
code

And in express I have my route:

const Uploads = require("./controllers/Uploads");

module.exports = app => {
  app.post("/upload", Uploads.index);
};

code

And within the controller I have:

// Load dependencies for file uploading to DO
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");

// Set S3 endpoint to DigitalOcean Spaces
const spacesEndpoint = new aws.Endpoint("nyc3.digitaloceanspaces.com");
const s3 = new aws.S3({
  endpoint: spacesEndpoint
});

// Image upload middleware
// Change bucket property to your Space name
const upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: "1410",
    acl: "public-read",
    key: function(request, file, cb) {
      console.log(file);
      cb(null, file.originalname);
    }
  })
}).array("upload", 1);

module.exports = {
  index(req, res) {
    console.log("backend hit");
    upload(req, res, function(error) {
      if (error) {
        console.log(error);
        return res.redirect("/error");
      }
      console.log("File uploaded successfully.");
      res.redirect("/success");
    });
  }
};

code

When I upload the image I actually get the "File uploaded successfully" message, but there is no file in my bucket when I check DigitalOcean

Any help is appreciated!

2 Answers

Hey friend,

That is an interesting one. As this isn't something I code in specifically, I'm thinking a bit high level here. Maybe it can stir up the right idea though. My thought is, can you have the code output a debug message that includes the raw response from our API? That may give some more insight into what is happening.

Kind Regards,
Jarland

  • Hello and thank you for your response!

    I wasn't exactly sure how to generate the message that you are looking for.

    I added console.log(res) right above console.log("File uploaded successfully.");

    I'm not sure if that provides any useful information or not, but this is what I got:

    ServerResponse {
      domain: null,
      _events: { finish: [Function: bound resOnFinish] },
      _eventsCount: 1,
      _maxListeners: undefined,
      output: [],
      outputEncodings: [],
      outputCallbacks: [],
      outputSize: 0,
      writable: true,
      _last: false,
      upgrading: false,
      chunkedEncoding: false,
      shouldKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: true,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: false,
      _headerSent: false,
      socket: 
       Socket {
         connecting: false,
         _hadError: false,
         _handle: 
          TCP {
            reading: true,
            owner: [Circular],
            onread: [Function: onread],
            onconnection: null,
            writeQueueSize: 0,
            _consumed: true },
         _parent: null,
         _host: null,
         _readableState: 
          ReadableState {
            objectMode: false,
            highWaterMark: 16384,
            buffer: [Object],
            length: 0,
            pipes: null,
            pipesCount: 0,
            flowing: true,
            ended: false,
            endEmitted: false,
            reading: true,
            sync: false,
            needReadable: true,
            emittedReadable: false,
            readableListening: false,
            resumeScheduled: false,
            destroyed: false,
            defaultEncoding: 'utf8',
            awaitDrain: 0,
            readingMore: false,
            decoder: null,
            encoding: null },
         readable: true,
         domain: null,
         _events: 
          { end: [Array],
            finish: [Function: onSocketFinish],
            _socketEnd: [Function: onSocketEnd],
            drain: [Array],
            timeout: [Function: socketOnTimeout],
            data: [Function: bound socketOnData],
            error: [Function: socketOnError],
            close: [Array],
            resume: [Function: onSocketResume],
            pause: [Function: onSocketPause] },
         _eventsCount: 10,
         _maxListeners: undefined,
         _writableState: 
          WritableState {
            objectMode: false,
            highWaterMark: 16384,
            finalCalled: false,
            needDrain: false,
            ending: false,
            ended: false,
            finished: false,
            destroyed: false,
            decodeStrings: false,
            defaultEncoding: 'utf8',
            length: 0,
            writing: false,
            corked: 0,
            sync: false,
            bufferProcessing: false,
            onwrite: [Function: bound onwrite],
            writecb: null,
            writelen: 0,
            bufferedRequest: null,
            lastBufferedRequest: null,
            pendingcb: 0,
            prefinished: false,
            errorEmitted: false,
            bufferedRequestCount: 0,
            corkedRequestsFree: [Object] },
         writable: true,
         allowHalfOpen: true,
         _bytesDispatched: 432,
         _sockname: null,
         _pendingData: null,
         _pendingEncoding: '',
         server: 
          Server {
            domain: null,
            _events: [Object],
            _eventsCount: 2,
            _maxListeners: undefined,
            _connections: 1,
            _handle: [Object],
            _usingSlaves: false,
            _slaves: [],
            _unref: false,
            allowHalfOpen: true,
            pauseOnConnect: false,
            httpAllowHalfOpen: false,
            timeout: 120000,
            keepAliveTimeout: 5000,
            _pendingResponseData: 0,
            maxHeadersCount: null,
            _connectionKey: '6::::8000',
            [Symbol(asyncId)]: 14 },
         _server: 
          Server {
            domain: null,
            _events: [Object],
            _eventsCount: 2,
            _maxListeners: undefined,
            _connections: 1,
            _handle: [Object],
            _usingSlaves: false,
            _slaves: [],
            _unref: false,
            allowHalfOpen: true,
            pauseOnConnect: false,
            httpAllowHalfOpen: false,
            timeout: 120000,
            keepAliveTimeout: 5000,
            _pendingResponseData: 0,
            maxHeadersCount: null,
            _connectionKey: '6::::8000',
            [Symbol(asyncId)]: 14 },
         _idleTimeout: 120000,
         _idleNext: 
          TimersList {
            _idleNext: [Circular],
            _idlePrev: [Circular],
            _timer: [Object],
            _unrefed: true,
            msecs: 120000,
            nextTick: false },
         _idlePrev: 
          TimersList {
            _idleNext: [Circular],
            _idlePrev: [Circular],
            _timer: [Object],
            _unrefed: true,
            msecs: 120000,
            nextTick: false },
         _idleStart: 22863,
         _destroyed: false,
         parser: 
          HTTPParser {
            '0': [Function: parserOnHeaders],
            '1': [Function: parserOnHeadersComplete],
            '2': [Function: parserOnBody],
            '3': [Function: parserOnMessageComplete],
            '4': [Function: bound onParserExecute],
            _headers: [],
            _url: '',
            _consumed: true,
            socket: [Circular],
            incoming: [Object],
            outgoing: null,
            maxHeaderPairs: 2000,
            onIncoming: [Function: bound parserOnIncoming] },
         on: [Function: socketOnWrap],
         _paused: false,
         read: [Function],
         _consuming: true,
         _httpMessage: [Circular],
         [Symbol(asyncId)]: 467,
         [Symbol(bytesRead)]: 0,
         [Symbol(asyncId)]: 469,
         [Symbol(triggerAsyncId)]: 467 },
      connection: 
       Socket {
         connecting: false,
         _hadError: false,
         _handle: 
          TCP {
            reading: true,
            owner: [Circular],
            onread: [Function: onread],
            onconnection: null,
            writeQueueSize: 0,
            _consumed: true },
         _parent: null,
         _host: null,
         _readableState: 
          ReadableState {
            objectMode: false,
            highWaterMark: 16384,
            buffer: [Object],
            length: 0,
            pipes: null,
            pipesCount: 0,
            flowing: true,
            ended: false,
            endEmitted: false,
            reading: true,
            sync: false,
            needReadable: true,
            emittedReadable: false,
            readableListening: false,
            resumeScheduled: false,
            destroyed: false,
            defaultEncoding: 'utf8',
            awaitDrain: 0,
            readingMore: false,
            decoder: null,
            encoding: null },
         readable: true,
         domain: null,
         _events: 
          { end: [Array],
            finish: [Function: onSocketFinish],
            _socketEnd: [Function: onSocketEnd],
            drain: [Array],
            timeout: [Function: socketOnTimeout],
            data: [Function: bound socketOnData],
            error: [Function: socketOnError],
            close: [Array],
            resume: [Function: onSocketResume],
            pause: [Function: onSocketPause] },
         _eventsCount: 10,
         _maxListeners: undefined,
         _writableState: 
          WritableState {
            objectMode: false,
            highWaterMark: 16384,
            finalCalled: false,
            needDrain: false,
            ending: false,
            ended: false,
            finished: false,
            destroyed: false,
            decodeStrings: false,
            defaultEncoding: 'utf8',
            length: 0,
            writing: false,
            corked: 0,
            sync: false,
            bufferProcessing: false,
            onwrite: [Function: bound onwrite],
            writecb: null,
            writelen: 0,
            bufferedRequest: null,
            lastBufferedRequest: null,
            pendingcb: 0,
            prefinished: false,
            errorEmitted: false,
            bufferedRequestCount: 0,
            corkedRequestsFree: [Object] },
         writable: true,
         allowHalfOpen: true,
         _bytesDispatched: 432,
         _sockname: null,
         _pendingData: null,
         _pendingEncoding: '',
         server: 
          Server {
            domain: null,
            _events: [Object],
            _eventsCount: 2,
            _maxListeners: undefined,
            _connections: 1,
            _handle: [Object],
            _usingSlaves: false,
            _slaves: [],
            _unref: false,
            allowHalfOpen: true,
            pauseOnConnect: false,
            httpAllowHalfOpen: false,
            timeout: 120000,
            keepAliveTimeout: 5000,
            _pendingResponseData: 0,
            maxHeadersCount: null,
            _connectionKey: '6::::8000',
            [Symbol(asyncId)]: 14 },
         _server: 
          Server {
            domain: null,
            _events: [Object],
            _eventsCount: 2,
            _maxListeners: undefined,
            _connections: 1,
            _handle: [Object],
            _usingSlaves: false,
            _slaves: [],
            _unref: false,
            allowHalfOpen: true,
            pauseOnConnect: false,
            httpAllowHalfOpen: false,
            timeout: 120000,
            keepAliveTimeout: 5000,
            _pendingResponseData: 0,
            maxHeadersCount: null,
            _connectionKey: '6::::8000',
            [Symbol(asyncId)]: 14 },
         _idleTimeout: 120000,
         _idleNext: 
          TimersList {
            _idleNext: [Circular],
            _idlePrev: [Circular],
            _timer: [Object],
            _unrefed: true,
            msecs: 120000,
            nextTick: false },
         _idlePrev: 
          TimersList {
            _idleNext: [Circular],
            _idlePrev: [Circular],
            _timer: [Object],
            _unrefed: true,
            msecs: 120000,
            nextTick: false },
         _idleStart: 22863,
         _destroyed: false,
         parser: 
          HTTPParser {
            '0': [Function: parserOnHeaders],
            '1': [Function: parserOnHeadersComplete],
            '2': [Function: parserOnBody],
            '3': [Function: parserOnMessageComplete],
            '4': [Function: bound onParserExecute],
            _headers: [],
            _url: '',
            _consumed: true,
            socket: [Circular],
            incoming: [Object],
            outgoing: null,
            maxHeaderPairs: 2000,
            onIncoming: [Function: bound parserOnIncoming] },
         on: [Function: socketOnWrap],
         _paused: false,
         read: [Function],
         _consuming: true,
         _httpMessage: [Circular],
         [Symbol(asyncId)]: 467,
         [Symbol(bytesRead)]: 0,
         [Symbol(asyncId)]: 469,
         [Symbol(triggerAsyncId)]: 467 },
      _header: null,
      _onPendingData: [Function: bound updateOutgoingData],
      _sent100: false,
      _expect_continue: false,
      req: 
       IncomingMessage {
         _readableState: 
          ReadableState {
            objectMode: false,
            highWaterMark: 16384,
            buffer: [Object],
            length: 0,
            pipes: null,
            pipesCount: 0,
            flowing: true,
            ended: true,
            endEmitted: true,
            reading: false,
            sync: false,
            needReadable: false,
            emittedReadable: false,
            readableListening: false,
            resumeScheduled: false,
            destroyed: false,
            defaultEncoding: 'utf8',
            awaitDrain: 0,
            readingMore: false,
            decoder: null,
            encoding: null },
         readable: false,
         domain: null,
         _events: {},
         _eventsCount: 0,
         _maxListeners: undefined,
         socket: 
          Socket {
            connecting: false,
            _hadError: false,
            _handle: [Object],
            _parent: null,
            _host: null,
            _readableState: [Object],
            readable: true,
            domain: null,
            _events: [Object],
            _eventsCount: 10,
            _maxListeners: undefined,
            _writableState: [Object],
            writable: true,
            allowHalfOpen: true,
            _bytesDispatched: 432,
            _sockname: null,
            _pendingData: null,
            _pendingEncoding: '',
            server: [Object],
            _server: [Object],
            _idleTimeout: 120000,
            _idleNext: [Object],
            _idlePrev: [Object],
            _idleStart: 22863,
            _destroyed: false,
            parser: [Object],
            on: [Function: socketOnWrap],
            _paused: false,
            read: [Function],
            _consuming: true,
            _httpMessage: [Circular],
            [Symbol(asyncId)]: 467,
            [Symbol(bytesRead)]: 0,
            [Symbol(asyncId)]: 469,
            [Symbol(triggerAsyncId)]: 467 },
         connection: 
          Socket {
            connecting: false,
            _hadError: false,
            _handle: [Object],
            _parent: null,
            _host: null,
            _readableState: [Object],
            readable: true,
            domain: null,
            _events: [Object],
            _eventsCount: 10,
            _maxListeners: undefined,
            _writableState: [Object],
            writable: true,
            allowHalfOpen: true,
            _bytesDispatched: 432,
            _sockname: null,
            _pendingData: null,
            _pendingEncoding: '',
            server: [Object],
            _server: [Object],
            _idleTimeout: 120000,
            _idleNext: [Object],
            _idlePrev: [Object],
            _idleStart: 22863,
            _destroyed: false,
            parser: [Object],
            on: [Function: socketOnWrap],
            _paused: false,
            read: [Function],
            _consuming: true,
            _httpMessage: [Circular],
            [Symbol(asyncId)]: 467,
            [Symbol(bytesRead)]: 0,
            [Symbol(asyncId)]: 469,
            [Symbol(triggerAsyncId)]: 467 },
         httpVersionMajor: 1,
         httpVersionMinor: 1,
         httpVersion: '1.1',
         complete: true,
         headers: 
          { host: 'localhost:8000',
            'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0',
            accept: 'application/json, text/plain, */*',
            'accept-language': 'en-US,en;q=0.5',
            'accept-encoding': 'gzip, deflate',
            referer: 'http://localhost:8080/',
            'content-type': 'application/json;charset=utf-8',
            'content-length': '2',
            origin: 'http://localhost:8080',
            connection: 'keep-alive',
            dnt: '1' },
         rawHeaders: 
          [ 'Host',
            'localhost:8000',
            'User-Agent',
            'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0',
            'Accept',
            'application/json, text/plain, */*',
            'Accept-Language',
            'en-US,en;q=0.5',
            'Accept-Encoding',
            'gzip, deflate',
            'Referer',
            'http://localhost:8080/',
            'Content-Type',
            'application/json;charset=utf-8',
            'Content-Length',
            '2',
            'Origin',
            'http://localhost:8080',
            'Connection',
            'keep-alive',
            'DNT',
            '1' ],
         trailers: {},
         rawTrailers: [],
         upgrade: false,
         url: '/upload',
         method: 'POST',
         statusCode: null,
         statusMessage: null,
         client: 
          Socket {
            connecting: false,
            _hadError: false,
            _handle: [Object],
            _parent: null,
            _host: null,
            _readableState: [Object],
            readable: true,
            domain: null,
            _events: [Object],
            _eventsCount: 10,
            _maxListeners: undefined,
            _writableState: [Object],
            writable: true,
            allowHalfOpen: true,
            _bytesDispatched: 432,
            _sockname: null,
            _pendingData: null,
            _pendingEncoding: '',
            server: [Object],
            _server: [Object],
            _idleTimeout: 120000,
            _idleNext: [Object],
            _idlePrev: [Object],
            _idleStart: 22863,
            _destroyed: false,
            parser: [Object],
            on: [Function: socketOnWrap],
            _paused: false,
            read: [Function],
            _consuming: true,
            _httpMessage: [Circular],
            [Symbol(asyncId)]: 467,
            [Symbol(bytesRead)]: 0,
            [Symbol(asyncId)]: 469,
            [Symbol(triggerAsyncId)]: 467 },
         _consuming: true,
         _dumped: false,
         next: [Function: next],
         baseUrl: '',
         originalUrl: '/upload',
         _parsedUrl: 
          Url {
            protocol: null,
            slashes: null,
            auth: null,
            host: null,
            port: null,
            hostname: null,
            hash: null,
            search: null,
            query: null,
            pathname: '/upload',
            path: '/upload',
            href: '/upload',
            _raw: '/upload' },
         params: {},
         query: {},
         res: [Circular],
         _parsedOriginalUrl: 
          Url {
            protocol: null,
            slashes: null,
            auth: null,
            host: null,
            port: null,
            hostname: null,
            hash: null,
            search: null,
            query: null,
            pathname: '/upload',
            path: '/upload',
            href: '/upload',
            _raw: '/upload' },
         sessionStore: 
          MemoryStore {
            domain: null,
            _events: [Object],
            _eventsCount: 2,
            _maxListeners: undefined,
            sessions: [Object],
            generate: [Function] },
         sessionID: 'ryhvN2OOWE8CRIKNNxSRojA1RbKvrNNI',
         session: Session { cookie: [Object] },
         _validationCustomMethods: { sanitizers: {}, validators: {} },
         _validationErrors: [],
         _asyncValidationErrors: [],
         validationErrors: [Function],
         asyncValidationErrors: [Function],
         getValidationResult: [Function],
         sanitizeBody: [Function],
         sanitizeParams: [Function],
         sanitizeQuery: [Function],
         sanitizeCookies: [Function],
         sanitizeHeaders: [Function],
         sanitize: [Function],
         checkBody: [Function],
         checkParams: [Function],
         checkQuery: [Function],
         checkCookies: [Function],
         checkHeaders: [Function],
         check: [Function],
         filter: [Function],
         assert: [Function],
         validate: [Function],
         _passport: { instance: [Object] },
         body: {},
         _body: true,
         length: undefined,
         read: [Function],
         route: Route { path: '/upload', stack: [Array], methods: [Object] } },
      locals: {},
      writeHead: [Function: writeHead],
      end: [Function: end],
      [Symbol(outHeadersKey)]: 
       { 'x-powered-by': [ 'X-Powered-By', 'Express' ],
         'access-control-allow-origin': [ 'Access-Control-Allow-Origin', '*' ] } }
    
    code
    

    I should also note that in my original post and how I have been working with the code, I have the headers: {"Content-Type": "multipart/form-data"} commented out in the axios post. From what I was reading, I may actually need to include that and uncomment it? If I do uncomment that code, I receive this error message on the server when I hit submit on the upload

    Error: Multipart: Boundary not found
        at new Multipart (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/busboy/lib/types/multipart.js:58:11)
        at Multipart (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/busboy/lib/types/multipart.js:26:12)
        at Busboy.parseHeaders (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/busboy/lib/main.js:71:22)
        at new Busboy (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/busboy/lib/main.js:22:10)
        at multerMiddleware (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/multer/lib/make-middleware.js:33:16)
        at index (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/controllers/Uploads.js:29:5)
        at Layer.handle [as handle_request] (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/layer.js:95:5)
        at next (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/route.js:137:13)
        at Route.dispatch (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/route.js:112:3)
        at Layer.handle [as handle_request] (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/layer.js:95:5)
        at /media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/index.js:281:22
        at Function.process_params (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/index.js:335:12)
        at next (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/express/lib/router/index.js:275:10)
        at cors (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/cors/lib/index.js:188:7)
        at /media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/cors/lib/index.js:224:17
        at originCallback (/media/rob/cdcda95a-f6c7-458a-abe5-b3f3a3629e8a/photo-wall/server/node_modules/cors/lib/index.js:214:15)
    
    code
    

I dont see where you put your access and secret key credentials when you're instantiating S3.

You should have gotten a 401 Unauthorized error with that code.

  • Hello and thank you for the reply!

    My credentials are locally on my machine (I followed the steps in the tutorial referenced in my first post to do that). I don't think that is the problem because I successfully uploaded a text file.

Have another answer? Share your knowledge.