Editor node.js file upload example error - A server error occurred while uploading the file

Editor node.js file upload example error - A server error occurred while uploading the file

CapamaniaCapamania Posts: 233Questions: 81Answers: 5
edited July 2019 in Editor

Editor node.js file upload example error - A server error occurred while uploading the file

I'm not getting the basic file upload example to run:

https://editor.datatables.net/manual/nodejs/upload

In the frontend I'm getting:

A server error occurred while uploading the file

... when trying to upload a file.

let {
    Editor,
    Field,
    Validate,
    Format,
    Options,
    Upload,
    promisify
} = require('datatables.net-editor-server');

let unlink = promisify(fs.unlink); // await version of unlink

var getBody = JSON.parse(req.body.json);

let editor = new Editor( db, 'news', 'id' )
.fields(
  new Field( 'news.id', 'id' ),
  new Field( 'news.cruser_id', 'cruser_id' ),
  new Field( 'news.image', 'image' ).setFormatter( Format.ifEmpty(null) ).upload(
      new Upload( __dirname + '/../public/uploads/{id}.{extn}' )
        .db('image', 'id', {
            fileName: Upload.Db.FileName,
            fileSize: Upload.Db.FileSize,
            web_path: '/uploads/{id}.{extn}',
            system_path: Upload.Db.SystemPath
        })
        .validator( Validate.fileSize(500000, 'Files must be smaller than 500K') )
                .validator(
                    Validate.fileExtensions(
                        ['png', 'jpg', 'gif'],
                        'Only image files can be uploaded (png, jpg and gif)'
                    )
                )
        .dbClean(async function(data) {
            for (let i = 0, ien = data.length; i < ien; i++) {
                await unlink(data[i].system_path);
            }
            return true;
        })
    )
)

await editor.process(getBody, req.files);

res.json(editor.data());

What am I missing and How can I solve this?

This question has an accepted answers - jump to answer

Answers

  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5
    edited July 2019

    js editor

    // Editor
    editor = new $.fn.dataTable.Editor( {
        ajax: {
            url: "/news/"
        },
        table: "#news"
        fields: [{
            label: "Image:",
            name: "image",
            type: "upload",
            display: function ( file_id ) {
                return '<img src="'+editor.file( 'image', file_id ).web_path+'"/>';
            },
            clearText: "Clear"
          }
    });
    
  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5

    Table structure for table image

    CREATE TABLE `image` (
      `id` int(11) UNSIGNED NOT NULL,
      `fileName` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `fileSize` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
      `web_path` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
      `system_path` varchar(255) COLLATE utf8_unicode_ci NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    ALTER TABLE `image`
      ADD PRIMARY KEY (`id`);   
    
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Does the console show anything where you are running the server? Hopefully it should be showing an error message - possibly about file permissions or something else.

    Allan

  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5
    edited July 2019

    I don't see anything suspicious. Also no debug info if I do editor.debug(true); The XHR is also OK. Yet, there I noticed compared to your example https://editor.datatables.net/examples/advanced/upload.html
    that the POST response after choosing a file in your case e.g. shows:

        data    []
        files   {…}
            files   {…}
                3   {…}
                    id  3
                    filename    Screenshot from 2019-07-18 18-29-47.png
                    filesize    180890
                    web_path    /upload/3.png
                    system_path /home/datat/public_html/editor/upload/3.png
        upload  {…}
            id  3 
    

    While mine shows the regular response as if I would (as usually) load the table:

        data    […]
        fieldErrors []
        files   {}
        options {…}
    

    ... so no files information ... plus the fronend comment: A server error occurred while uploading the file.

    The permissioning of the folders is like:

    public  rwx r-x r-x
        images  rwx r-x r-x 
    
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    In the NodeJS libraries you need to use debug: true in your Knex configuration to have it dump information about the SQL statements out to the console.

    Allan

  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5

    There I also don't find anything suspicious cry ...

    POST /v1/news/ 200 
    
    { method: 'select',
      options: {},
      timeout: false,
      cancelOnTimeout: false,
      bindings: [],
      __knexQueryUid: '5723523d-a2b4-43453-9535f-113459c9955',
      sql:
       'select `id` as `id`, `news`.`id` as `news.id`, `news`.`title` as `news.title`, `news`.`author` as `news.author`, `news`.`file` as `news.file`, `news`.`cruser_id` as `news.cruser_id` from `news` }
    { method: 'select',
      options: {},
      timeout: false,
      cancelOnTimeout: false,
      bindings: [],
      __knexQueryUid: '5723523d-a2b4-43453-9535f-113459c9955',
      sql:
       'select distinct `author`, `author` from `news` order by `author` asc' }
    Editor {
      _fields:
       [ Field {
           _get: true,
           _set: 1,
           _validator: [],
           _xssFormat: true,
           _name: 'news.id',
           _dbField: 'news.id' },
         Field {
           _get: true,
           _set: 1,
           _validator: [Array],
           _xssFormat: true,
           _name: 'news.title',
           _dbField: 'news.title' },
         Field {
           _get: true,
           _set: 1,
           _validator: [Array],
           _xssFormat: true,
           _name: 'news.author',
           _dbField: 'news.author',
           _opts: [Options] },
         Field {
           _get: true,
           _set: 1,
           _validator: [],
           _xssFormat: true,
           _name: 'news.file',
           _dbField: 'news.file',
           _setFormatter: [Function],
           _upload: [Upload] },
         Field {
           _get: true,
           _set: 1,
           _validator: [],
           _xssFormat: true,
           _name: 'news.cruser_id',
           _dbField: 'news.cruser_id' } ],
      _idPrefix: 'row_',
      _join: [],
      _pkey: [ 'id' ],
      _table: [ 'news' ],
      _readTableNames: [],
      _transaction: false,
      _where: [],
      _leftJoin: [],
      _out:
       { data:
          [ [Object],
            [Object],
            [Object],
            [Object] ],
         fieldErrors: [],
         draw: undefined,
         files: {},
         options: { 'news.author': [Array] },
         recordsTotal: undefined,
         recordsFiltered: undefined },
      _events: [ preCreate: [ [Function] ], preEdit: [ [Function] ] ],
      _validators: [],
      _tryCatch: false,
      _debug: true,
      _debugInfo: [],
      _leftJoinRemove: false,
      _schema: null,
      _db:
       { [Function: knex]
         Promise:
          { [Function: Promise]
            TypeError: [Function: TypeError],
            RangeError: [Function: RangeError],
            CancellationError: [Function: SubError],
            TimeoutError: [Function: SubError],
            OperationalError: [Function: OperationalError],
            RejectionError: [Function: OperationalError],
            AggregateError: [Function: SubError],
            _peekContext: [Function],
            onPossiblyUnhandledRejection: [Function],
            onUnhandledRejectionHandled: [Function],
            longStackTraces: [Function],
            hasLongStackTraces: [Function],
            config: [Function],
            getNewLibraryCopy: [Function],
            is: [Function],
            fromCallback: [Function],
            fromNode: [Function],
            all: [Function],
            cast: [Function],
            fulfilled: [Function],
            resolve: [Function],
            rejected: [Function],
            reject: [Function],
            setScheduler: [Function],
            pending: [Function],
            defer: [Function],
            method: [Function],
            try: [Function],
            attempt: [Function],
            bind: [Function],
            PromiseInspection: [Function: PromiseInspection],
            join: [Function],
            Promise: [Circular],
            version: '3.5.4',
            map: [Function],
            using: [Function],
            delay: [Function],
            coroutine: [Function],
            spawn: [Function],
            promisify: [Function],
            promisifyAll: [Function],
            props: [Function],
            race: [Function],
            reduce: [Function],
            settle: [Function],
            some: [Function],
            _SomePromiseArray: [Function: SomePromiseArray],
            filter: [Function],
            each: [Function],
            mapSeries: [Function: PromiseMapSeries],
            any: [Function],
            noConflict: [Function: noConflict] },
         queryBuilder: [Function: queryBuilder],
         raw: [Function: raw],
         batchInsert: [Function: batchInsert],
         transaction: [Function: transaction],
         initialize: [Function: initialize],
         destroy: [Function: destroy],
         ref: [Function: ref],
         _events: [Object: null prototype] {},
         _eventsCount: 0,
         _maxListeners: undefined,
         setMaxListeners: [Function: setMaxListeners],
         getMaxListeners: [Function: getMaxListeners],
         emit: [Function: emit],
         addListener: [Function: addListener],
         on: [Function: addListener],
         prependListener: [Function: prependListener],
         once: [Function: once],
         prependOnceListener: [Function: prependOnceListener],
         removeListener: [Function: removeListener],
         off: [Function: removeListener],
         removeAllListeners: [Function: removeAllListeners],
         listeners: [Function: listeners],
         rawListeners: [Function: rawListeners],
         listenerCount: [Function: listenerCount],
         eventNames: [Function: eventNames],
         with: [Function],
         select: [Function],
         as: [Function],
         columns: [Function],
         column: [Function],
         from: [Function],
         fromJS: [Function],
         into: [Function],
         withSchema: [Function],
         table: [Function],
         distinct: [Function],
         join: [Function],
         joinRaw: [Function],
         innerJoin: [Function],
         leftJoin: [Function],
         leftOuterJoin: [Function],
         rightJoin: [Function],
         rightOuterJoin: [Function],
         outerJoin: [Function],
         fullOuterJoin: [Function],
         crossJoin: [Function],
         where: [Function],
         andWhere: [Function],
         orWhere: [Function],
         whereNot: [Function],
         orWhereNot: [Function],
         whereRaw: [Function],
         whereWrapped: [Function],
         havingWrapped: [Function],
         orWhereRaw: [Function],
         whereExists: [Function],
         orWhereExists: [Function],
         whereNotExists: [Function],
         orWhereNotExists: [Function],
         whereIn: [Function],
         orWhereIn: [Function],
         whereNotIn: [Function],
         orWhereNotIn: [Function],
         whereNull: [Function],
         orWhereNull: [Function],
         whereNotNull: [Function],
         orWhereNotNull: [Function],
         whereBetween: [Function],
         whereNotBetween: [Function],
         andWhereBetween: [Function],
         andWhereNotBetween: [Function],
         orWhereBetween: [Function],
         orWhereNotBetween: [Function],
         groupBy: [Function],
         groupByRaw: [Function],
         orderBy: [Function],
         orderByRaw: [Function],
         union: [Function],
         unionAll: [Function],
         having: [Function],
         havingRaw: [Function],
         orHaving: [Function],
         orHavingRaw: [Function],
         offset: [Function],
         limit: [Function],
         count: [Function],
         countDistinct: [Function],
         min: [Function],
         max: [Function],
         sum: [Function],
         sumDistinct: [Function],
         avg: [Function],
         avgDistinct: [Function],
         increment: [Function],
         decrement: [Function],
         first: [Function],
         debug: [Function],
         pluck: [Function],
         clearSelect: [Function],
         clearWhere: [Function],
         clearOrder: [Function],
         insert: [Function],
         update: [Function],
         returning: [Function],
         del: [Function],
         delete: [Function],
         truncate: [Function],
         transacting: [Function],
         connection: [Function],
         client:
          Client_MySQL {
            config: [Object],
            logger: [Logger],
            connectionSettings: [Object],
            driver: [Object],
            pool: [Pool],
            valueForUndefined: [Raw],
            _events: [Object],
            _eventsCount: 4,
            makeKnex: [Function: makeKnex] } },
      _processData: {},
      _uploadData: null,
      _formData: null } 
    
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Editor might be swallowing the error in order to present a nice user error - change your process() line to be:

    await editor.tryCatch(false).process(getBody, req.files);
    

    Allan

  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5

    Still nothing. Btw ... shouldn't req.files have any values? It's still undefined even after choosing a file:

    var getFiles = req.files;
    console.log('getFiles:');
    console.log(getFiles);
    
    getFiles:
    undefined
    { method: 'select',
      options: {},
      timeout: false,
      cancelOnTimeout: false,
      bindings: [],
      __knexQueryUid: '5723523d-a2b4-43453-9535f-113459c9955',
      sql:
       'select `id` as `id`, `news`.`id` as `news.id`, `news`.`title` as `news.title`, `news`.`author` as `news.author`, `news`.`file` as `news.file`, `news`.`cruser_id` as `news.cruser_id` from `news` }
    { method: 'select',
      options: {},
      timeout: false,
      cancelOnTimeout: false,
      bindings: [],
      __knexQueryUid: '5723523d-a2b4-43453-9535f-113459c9955',
      sql:
       'select distinct `author`, `author` from `news` order by `author` asc' }
    POST /v1/news/ 200 20.919 ms - 32239
    
  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5

    When I check the editor ajax:

    editor = new $.fn.dataTable.Editor( {
        ajax: {
            url: "/new/",
            data: function ( d ) {
                console.log('Ajax d:')
                console.log(d);
                return JSON.stringify( d );
            }
        },
     ...
     });
    

    and submit it without a image I can see in fe console as expected:

    Ajax d:
        Object { action: "edit", data: {…} }
    

    But when I upload a picture I get:

    Ajax d:
        {}
    ​       <prototype>: Object { … }   
    
  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5
    edited July 2019

    And ...

    editor_news.on( 'preUpload', function ( e, fieldName, file, ajaxData ) {
        console.log(ajaxData);
        console.log(fieldName);
        console.log(file);
    });
    

    ... shows in the front end:

    FormData {  }
    news.file
    File
        lastModified: 1559814724000
        name: "picture.png"
        size: 3874
        type: "image/png"
        webkitRelativePath: ""
        <prototype>: FilePrototype { name: Getter, lastModified: Getter, webkitRelativePath: Getter, … }
    

    Looks like FormData{} is empty.

  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5

    Might the problem be that I have two different instances ... one for the backend and another for the frontend?!

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Answer ✓

    Btw ... shouldn't req.files have any values

    It should when you upload a file yes.

    Have you got the express-busboy module installed for your node app?

    let bb = require( 'express-busboy' );
    
    bb.extend( app, {
        upload: true
    } );
    

    Allan

  • CapamaniaCapamania Posts: 233Questions: 81Answers: 5

    That was it, many thanks!

This discussion has been closed.