Search code examples
javascriptc#asp.nethtml5-canvastouch

Creating a canvas image from an iPad


When sending an image of a canvas from an iPad or other touch-based device, my canvas-based signature pad does not save the image to the specified directory. Despite the image data sending perfectly fine when testing from Visual Studio and from the live website on a regular Windows PC, the iPad seems incapable of sending the Base64 code through the JavaScript function.

When creating the reprex for this site, I noticed that I needed to comment out a line in RouteConfig.cs (thanks to Sanjay Sharma) before the app was even able to send an image to my local machine. Since then, I tried seeing if there was any sort of redirect issue that would have caused problems when working from the iPad, but I have found nothing to suggest this.

Built as an ASP.NET Web Forms Site (Visual C# > Web > Previous Versions), this page contains a canvas that works with touch and desktop interfaces. The code used to make the canvas (called upon in "Drawpad") is based on the code found here:

<body data-rsssl="1" onload="DrawPad()">
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        <input type="hidden" id="imageData" runat="server" class="image-data" />
        <canvas id="myCanvas" width="500" height="100"></canvas>
        <br />
        <asp:Button ID="btnSave" runat="server" Text="Submit" />
    </form>
    <script type="text/javascript" id="jsDraw">
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        // function DrawPad() {...} // see the bullet points for this one

        $("#btnSave").click(function () {
            var image = document.getElementById('myCanvas').toDataURL("image/png");
            image = image.replace('data:image/png;base64,', '');
            $.ajax({
                type: 'POST',
                url: 'Default.aspx/UploadImage',
                data: '{ imageData : "' + image + '" }',
                contentType: 'application/json; charset=utf-8',
                dataType: 'json'
            });
        });
    </script>
</body>

This is then called in the C# file at the push of a button (specifically btnSave):

public static void UploadImage(string imageData)
{
    using (FileStream fs = new FileStream(@"C:\\Users\\Alex\\Documents\\testpic.png", FileMode.Create))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            bw.Write(Convert.FromBase64String(imageData));
            bw.Close();
        }
    }
}

The app should stream the image to the predetermined directory. Something in that process (whether the BinaryWriter, FileStream, or something else) is causing the data to not be written, despite having direct access to the image's Base64 code. As a result, attempting to sign the form and send the image results in a SQL-based parameter error when attempting to load the file's name into the required table; because the file is not being created, there is no file name, and therefore no way to submit data into the table and nothing to call back to even if there were a file name.

My best guess is that when submitting from the iPad, something is causing the UploadImage() method to be ignored completely, but I have no idea what.


Solution

  • The issue is actually not a JavaScript error, but a C# error. Here is what UploadImage() should be doing:

    public static void UploadImage(string imageData)
    {
        var impersonator = new ImpersonationHelper("<domain>", "<username>", "<password>");
        impersonator.StartImpersonating();
        string path = @"\\\\PATH\\TO\\FILE\\DIRECTORY\\";
        if (!Directory.Exists(path)) // creates a new folder if this one doesn't currently exist yet
            Directory.CreateDirectory(path);
        string fileNameWithPath = path + "SIG" + DateTime.Now.ToString("MMddyyyy") + "-" + rand + ".png"; // where rand is randomly-generated elsewhere
        byte[] imageBytes = Convert.FromBase64String(imageData);
        File.WriteAllBytes(fileNameWithPath, imageBytes);
        impersonator.StopImpersonating();
    }
    

    Rather than use a FileWriter, the code should split the Base64 string into a series of bytes so that it can be written to a file regardless of which device is being used to send the image data. I have confirmed that this works from an iPad without any issue.