0

Currently struggling to do this, and I'm now thinking that it may be better to handle this server-side but here goes.

I currently creating a small app which get a users drawn signature which is using HTML canvas and uploads it to Dropbox using their API:

const Canvas = () => {
    const [imageURL, setImageURL] = useState('');

    window.addEventListener('load', () => {
        const canvas = document.querySelector('#canvas');
        const ctx = canvas.getContext('2d');
        const rect = canvas.getBoundingClientRect();
        const clear = document.getElementById('clear');

        // Resizing
        canvas.height = 300;
        canvas.width = 600;

        // Vars
        let painting = false;

        function startPosition(e) {
            painting = true;
            draw(e);
        }

        function finishedPosition() {
            painting = false;
            ctx.beginPath();
            const newSignature = canvas.toDataURL();
            setImageURL(newSignature);
        }

        function draw(e) {
            if (!painting) return;
            ctx.lineWidth = 2;
            ctx.lineCap = 'round';

            ctx.lineTo(e.clientX - rect.left, e.clientY - rect.top);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(e.clientX - rect.left, e.clientY - rect.top);
        }

        function clearCanvas() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            setImageURL('');
        }

        // Listeners
        canvas.addEventListener('mousedown', startPosition);
        canvas.addEventListener('mouseup', finishedPosition);
        canvas.addEventListener('mousemove', draw);
        clear.addEventListener('click', clearCanvas);
    });

    function uploadFile() {
        axios.post(
            'https://content.dropboxapi.com/2/files/upload',
            { data: imageURL },
            {
                headers: {
                    Authorization: 'Bearer TOKEN_HERE',
                    'Content-Type': 'application/octet-stream',
                    'Dropbox-API-Arg': '{"path": "/signature","mode": "add","autorename": true,"mute": false}',
                },
            },
        );
    }

    return (
        <Fragment>
            <div className={styles.wrapper}>
                <canvas id="canvas" className={styles.nwicanvas} />
                <Button buttonId="clear">Clear</Button>
                <h3 className={styles.title}>Signature preview</h3>
                {imageURL !== '' ? <img src={imageURL} alt="signature" /> : <p>No signature drawn.</p>}
                {imageURL !== '' ? <a download="signature.png" href={imageURL}>Download</a> : null}
                {imageURL !== '' ? <Button onClick={uploadFile}>Upload</Button> : null}
            </div>
        </Fragment>
    );
};

This works to the point of creating a base64 image with the .toDataURL() function which works great for displaying the preview to the user, however when I send to data up to Dropbox it ends being a corrupted image. I've also tried converting the base64 image to a blob but I get another corrupted image.

I don't suppose it would be possible to convert this base64 image to raw binary data in Javascript and send that up instead?

Any help is very appreciated.

gman
  • 100,619
  • 31
  • 269
  • 393
  • Blob works fine for me. You might be misunderstaning the `axios.post` API. You are not sending the raw data, but a an object with a `data` property in your code in this line: `{ data: imageURL }`. – Petr Srníček Jul 18 '19 at 09:39
  • @PetrSrníček Raw data is what I needed. You're right. That's been solved using the `toBlob` method. Thanks. – Charlie Coplestone Jul 18 '19 at 09:48

1 Answers1

1

BLOB (Binary Large Object) is just that - binary data. I would suggest sticking to that instead of data URLs. It works like a charm for me:

function uploadToDropbox(data) {
  const config = {
    headers: {
      Authorization: 'Bearer <token here>',
      'Content-Type': 'application/octet-stream',
      'Dropbox-API-Arg': '{"path": "/test.png","mode": "add","autorename": true,"mute": false}',
    },
  };

  axios.post('https://content.dropboxapi.com/2/files/upload', data, config);
}

canvas.toBlob(uploadToDropbox, 'image/png');
Petr Srníček
  • 2,296
  • 10
  • 22
  • I just realized that availability of [`HTMLCanvasElement/toBlob()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) is rather limited. It is not availble in Edge and older browsers, so you might need to go through data URLs after all. Several solutions for converting data URLs to blobs are provided in this thread: https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript – Petr Srníček Jul 18 '19 at 09:55
  • Or use a polyfill for canvas.toBlob (not mine): https://jsfiddle.net/IsArlekin/obv5j64f/7/ – Petr Srníček Jul 18 '19 at 09:59
  • Yeah, I noticed this too. There is however a method specfically for Edge/IE called `msToBlob()`. Not sure how hacky this is though. – Charlie Coplestone Jul 18 '19 at 09:59