menu

Friday, March 7, 2014

Customize nicEdit Image upload to support uploading images to your server


Hey all,

So the last week was awesome, I ended up doing some crazy stuff. But this isnt one of them :D Anyway I was told by a friend that he need a WYSIWYG editor for his project. Naturally nicEdit was my choice. Yet again he needed the support of adding image to the content as well, I mean uploading. So I searched and luckily nicEdit has an upload plugin. So the bad newss? Bad news is it's core is designed to upload the image to imgur and attach the url given by their API.


Problem?

All my images would be gone to imgur, I really dont have much problem with that except that every content I post would be depending on the servers of imgur. What if imgur decided that my content is violating their TOS after sometime? :O (Just kidding, need to make a point)

So I hacked the script a bit and saw that their upload mechanism which is AJAX based isnt overly complicated.Best way to use our server to keep images is to make a php script that would mimic imgur API. Wasn't that hard. First let's see how the existing nicEdit upload works.

nicEdit upload process

1) User upload image
2) nicEdit make a creates a POST request to the imgur API
3) API key is added to request
4) Img is added as if it's being uploaded using a HTML form (POST content is send as a multipart form)
5) Request is send to imgur, if everything is fine imgur API responds with a JSON object containing different info about the image
6) nicEdit attach the image using the link send found in the JSON object

Writing something to mimic the imgur API

Script will be written in php but you're free to make it in any language as long as you correctly capture the request and return a correctly formatted JSON object

Request

Request as I mentioned before is as if it's comming from a HTML file upload form. Basically your image data would be available from $_FILES array. From there on we follow the simple php uploader mechanism that is,

1) Check whether the image is a valid image type, to prevent attacks
2) Move the image file from the php temp directory to our img directory
3) Rename it with something unique, I usually do random() or md5() functions for the new name
4) Get the URL to the uploaded image

NOTE:

At this stage you could choose between giving the user a direct URL to the image or making another php script that would return the image data with headers. Well it's more secure, but too much of a trouble. I will continue as if we are giving the direct link to the image.

Response

This is indeed the tricky part. Even if we return the URL to the nicEdit plugin it won't work since it's expecting a JSON object from imgur and I dont want to hack apart everything from the script (No one does :S). This is how the return JSON object would look like (Included ONLY the parts important to ur)

Response (JSON Object)
    - upload => (Array)
                          - links => (Array)
                                           - original =>  (URL of img)
                         - image => (Array)
                                           - width => width of image
                                           - height => height of image


Rest is just simple, we make a JSON object just like this and return :) So let's start

Code

img.php


  1. <?php
  2. //Check if we are getting the image
  3. if(isset($_FILES['image'])){
  4.         //Get the image array of details
  5.         $img = $_FILES['image'];       
  6.         //The new path of the uploaded image, rand is just used for the sake of it
  7.         $path = "upload/" . rand().$img["name"];
  8.         //Move the file to our new path
  9.         move_uploaded_file($img['tmp_name'],$path);
  10.         //Get image info, reuiqred to biuld the JSON object
  11.         $data = getimagesize($path);
  12.         //The direct link to the uploaded image, this might varyu depending on your script location    
  13.         $link = "http://$_SERVER[HTTP_HOST]"."/nicedit/".$path;
  14.         //Here we are constructing the JSON Object
  15.         $res = array("upload" => array(
  16.                                 "links" => array("original" => $link),
  17.                                 "image" => array("width" => $data[0],
  18.                                                  "height" => $data[1]
  19.                                                 )                              
  20.                     ));
  21.         //echo out the response :)
  22.         echo json_encode($res);
  23. }
  24. ?>


NOTE:

1) All image are uploaded to a directory named upload, so make sure that dir exists and is CHMOD to 777 (Given read/write permission)
2) Change line 14 to suit your server path

Now we need to tell nicEdit that we are having our own upload script, luckily it's easy.

Open the nicEdit.js (if you download the whole thing with nicUpload plugin).
Find the line starting like xhr.open("POST"... 
Replace it with

xhr.open("POST", "http://example.com/path_to_above_script/img.php");


DONE ! Now try uploading something and it would go directly to your server :)

Be back soon !

EDIT

Some of the comments below suggested that the response array should be changed like following for it to work correctly. I think it should be an issue with the latest nicEdit script. So if you are having troubles please try with this code for the '$res' array
Special thanks for jjsteing for pointing the new code


  1.         $res = array("data" => array(
  2.                                 "links" => $link),
  3.                                 "width" => $data[0],
  4.                                 "height" => $data[1]
  5.                                      ));                         

51 comments:

  1. sorry it alert failed to upload image

    ReplyDelete
    Replies
    1. might be a problem with server side, can you show me your code?

      Delete
    2. upload your whole code to server

      Delete
  2. Increible, funciono a la perfeccion.

    ReplyDelete
  3. Awesome brother... thankssssss.........

    ReplyDelete
  4. thanks a lot, it is really halp me

    ReplyDelete
  5. THX! You are my hero ManZzup!!!

    ReplyDelete
  6. Can not find "xhr.open" please help .

    ReplyDelete
    Replies
    1. any link to your nidEdit js? they may have changed it in their latest release, but would be easy to fix. Try searching for the imgur URL

      Delete
    2. Could you please upload the nicEdit.js that you are using? I have checked all archived versions since the upload feature was added (via webarchive.org), but none contain "xhr.open" phrase.

      Delete
    3. you need to download the development version
      in here check the nicUpload tick and download then "uncompressed nicEdit for development"
      http://nicedit.com/download.php

      if u downlod the compressed one, the line is as C.open("POST",..

      this is to minify the file size

      Delete
  7. How could I have missed that var names are changed upon minimisation!? Thank you very much - it works like a charm!!!

    ReplyDelete
  8. when i upload, alert " object ProgressEvent "

    ReplyDelete
    Replies
    1. where exactly u are getting that error?

      Delete
    2. I'm having the same problem in Firefox.

      Delete
  9. Hi,

    I did it exactly like you described but the the error 'Failed to upload image'.

    Here's a part of my code that seems to break:

    var fd = new FormData(); // https://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/
    fd.append("image", file);
    fd.append("key", "b7ea18a4ecbda8e92203fa4968d10660");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://localhost:8080/acp/functions/nicImageUpload.php");

    xhr.onload = function() {
    console.log(3.5);
    try {
    var res = JSON.parse(xhr.responseText);
    } catch(e) {
    console.log(3.54);
    return this.onError();
    console.log(3.6);
    }
    this.onUploaded(res.upload);

    (Yes, my XAMPP is on Port 8080)

    When I debug, the console throws out 3.5 and 3.54 but not 3.6. So it crashes there I believe.

    Any suggestions?

    ReplyDelete
  10. How could i use your script using "upload URL" ? I am trying to, but only upload was successful, but the image is not getting attached to the editor

    ReplyDelete
    Replies
    1. if it's not getting attached, it should be an issue with the postback object, that object consists of the details of where the img was uploaded. Can you provide with more details?

      Delete
  11. Nice work man. Worked on the first attempt!

    ReplyDelete
  12. services JSON for PHP 4.x:
    http://www.epigroove.com/blog/how-to-use-json-in-php-4-or-php-51x

    so, add this :

    if (!function_exists('json_encode')) {
    function json_encode($res) {
    require_once 'JSON.php';
    $json = new Services_JSON;
    return $json->encode($res);
    }
    }

    //before:

    echo json_encode($res);

    ReplyDelete
  13. Hi... I am having the same issue as CACA[COBRA]...
    only upload was successful, but the image is not getting attached to the editor - See more at: http://manzzup.blogspot.com/2014/03/customize-nicedit-image-upload-to.html#sthash.97qO1HQk.dpuf..and the progress bar continuously shows progress

    ReplyDelete
  14. Change on img.php:

    $res = array("data" => array(
    "link" => $link,
    "width" => $data[0],
    "height" => $data[1]));

    And all is working good :)

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. thanks, is very helpful
    this code for CodeIgniter:

    <?php
    defined('BASEPATH') OR exit('No direct script access allowed');
    class NiceUpload extends CI_Controller {
    function __construct() {
    parent::__construct();
    }

    public function index(){
    $config = array(
    'upload_path' => './assets/files/upload/',
    'allowed_types' => "jpg",
    'overwrite' => TRUE,
    'max_size' => "2560",
    'file_name' => md5(date('YmdHis')).'.jpg'
    );
    $this->load->library('upload', $config);
    $this->upload->do_upload('image');
    $data = array(
    'width'=>$this->upload->data('image_width'),
    'height'=>$this->upload->data('image_height'),
    'file_name'=>$this->upload->data('file_name')
    );
    $link = base_url().'assets/files/upload/'.$data['file_name'];
    $res = array("data" => array(
    'link' => $link,
    'width' => $data['width'],
    'height' => $data['height'])
    );
    echo json_encode($res);
    }
    }

    ReplyDelete
  17. i followed the instructions but now the image is uploaded to server but the loading bar keeps going on without stopping. any idea what could go wrong?

    ReplyDelete
    Replies
    1. exact same issue that is happening for me, image is uploaded to server but loading bar just keeps going

      Delete
    2. possible reason is that the return object from the server is different than what the script is expecting, i guess due to changes in newer versions of the script. If the code in EDIT section above doesnt work can you send me the exact nicEdit version you are using?

      Delete
  18. It shows nicEdit.js:1426 Uncaught TypeError: Cannot read property 'error' of undefined

    ReplyDelete
  19. Replies
    1. please refer to comments above, the updated nicEdit version might have different ways of handling the image data
      so in that case you need to use a mention specified in the comments

      Delete
  20. Hi i got

    "TypeError: D is undefined
    if (D.error) {" ?

    Image uploaded the specific path but not showing with in the editor?

    Also see below if i change in image.php this below code

    $res = array("data" => array(
    "link" => $link,
    "width" => $data[0],
    "height" => $data[1]));

    Even progress bar also not showing in the editor.

    Any Help?

    ReplyDelete
    Replies
    1. did you try with an earlier version of nicedit? this article seems to be not compatible with the newer versions

      Delete
  21. I can never thank you enough for this :D Thank you so so much

    ReplyDelete
    Replies
    1. array("data" => array(
      "link" => $link,
      "width" => $data[0]
      ));
      is the current expected response. and rest should be working perfectly. version 0.9 r25

      Delete
  22. Hi my nicedit version is 0.9 r25 it's released on 4th oct 2015.
    and i have a error while upload image on my own server

    actually image upload is specific folder is success but image uploader is continuously working and not stopping after image upload so can anyone help me please.
    my nicedit and img.php code is below.

    ReplyDelete
  23. -------------- nicEdit.js----------------
    /* START CONFIG */
    var nicUploadOptions = {
    buttons : {
    'upload' : {name : 'Upload Image', type : 'nicUploadButton'}
    }

    };
    /* END CONFIG */

    var nicUploadButton = nicEditorAdvancedButton.extend({
    nicURI : '/include/editor/image.php',
    errorText : 'Failed to upload image',

    ReplyDelete
  24. xhr.open("POST", "http://localhost/include/editor/image.php");

    ReplyDelete
  25. thanks, this working perfectly.

    I don't find the xhr.open, but I change the "nicURI" path and work.

    ReplyDelete
  26. script can upload the image, but bar never stops, and it doesn´t show the image in the editor.
    Anyone can help?

    ReplyDelete
  27. I've used your suggestions successfully in the past and it continues to work if I upload to imgur. However, on an Azure VM running IIS 7.5 - the image uploads successfully but doesn't show properly giving an Error 402. If I attempt to display image in a browser, I get the same error. If I manually change the permissions on the image just uploaded, it displays fine. If I delete the image from the folder and upload it again, same issue. I know this is a permissions issue but I can't seem to find the solution no matter what permissions I give the folder. Anyone have any ideas?

    Thanks

    ReplyDelete
    Replies
    1. Try changing the permissions of the upload directory to 777, which would provide read / write permissions to the directory, so all files in it will inherit it

      Delete