Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 9 of 9
  1. #1
    New to the CF scene
    Join Date
    Aug 2019
    Location
    Indiana
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Saving an canvas image from an iPad

    Heya fellow devs!

    I've been trying to make a digital signature pad for the past few weeks, but have been running into the issue of saving canvas "images" to the server from a touch interface. 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 static JavaScript function I've set in the C# files. However, rather than give me a standard error screen, submitting a signature from the iPad creates an empty .png file -- the likes of which Windows 10 cannot open because "it looks like we don't support this file format." To add to this, I'm given a blank document when opening the file in Notepad.

    The code I've written to create an image from a signature can be seen below. Please note the "function DrawPad() {}" -- which was made afollowing these links:

    Code:
    <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>
    At the risk of getting slapped on the wrist for posting C# code on a JS board (new guy -- sorry!), here is what happens on the ASP side when btnSave is pressed:
    Code:
    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();
            }
        }
    }
    I've asked others whether this issue is related to the FileStream or BinaryWriter or something within the JavaScript side of this app, but so far the most I have gotten back is a comment about how I should utilize the iOS "Files" app and directory when (and I should stress this clearly) the work I'm doing here is NOT for a mobile app, but for a website that users will access from a mobile device. And I would like to stress one more time: this web app works perfectly fine on a desktop environment with a modern browser, but not on any mobile device I've tested on -- and I cannot wrap my head around why this is.

    One other thing: because I heard the users around here want to see the error messages, I'm going to paste below what I got. Note that none of these are actually "errors." You can also see a screenshot of this webpage's default state in the attachments.
    -annotation-2019-08-06-103322.png
    Attached Thumbnails Attached Thumbnails -img_0013.jpg  

  2. #2
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Location
    Here
    Posts
    3,980
    Thanks
    58
    Thanked 710 Times in 705 Posts
    yes, thank you for the error messages. What you heard is right - we do love them around here.

    I agree that those messages are probably not critical, but they are also easy to fix and you might as well because badly formed HTML can backfire on you in unexpected ways.

    From the links provided I can see that the last one does have a critical error
    Code:
    $(".image-data").value(canvas.toDataURL());
    should be
    Code:
    $(".image-data").val(canvas.toDataURL());
    but if as you say you have this working on desktop you no doubt caught that one already.

    Trying to strip it back to basics, we can see that this works:
    Code:
     <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>title</title>
      </head>
      <body>
      <canvas id="myCanvas" width="200" height="200"></canvas>
    <br/>
    <input type="" id="imageData" class="image-data"/>
    <input type="submit" name="saveButton" id="saveButton" value="Save Drawing">
    <script
                  src="https://code.jquery.com/jquery-1.12.4.min.js"
                  integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
                  crossorigin="anonymous"></script>
    <script>
    $(function() {
        var canvas = document.getElementById('myCanvas');
    
        if(canvas.getContext) {
            var context = canvas.getContext('2d');
         
            context.strokeStyle = "#000000";
            context.fillStyle = "#0000FF";
            context.beginPath();
            context.arc(100,100,50,0,Math.PI*2,true);
            context.closePath();
            context.stroke();
            context.fill();
         }
        
        // button
        $("#saveButton").click(function(e) {
            var lnk = document.createElement("a");
            lnk.download = "image.png";
            lnk.href = document.getElementById("myCanvas").toDataURL("image/png").replace(/^data:image\/[^;]/, 'data:application/octet-stream');
            lnk.appendChild(document.createTextNode("click"));
            document.body.append(lnk)
        });
    });
    </script>
    </body>
    </html>
    The only real difference being the "replace" line

    I don't have an iPad, but you could test if the above works on the iPad (or just amend your code on the replace line and see if that fixes it).

  3. #3
    New to the CF scene
    Join Date
    Aug 2019
    Location
    Indiana
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Thank you so much for the reprex code! The only issue I have with it is that I'm being told "Object doesn't support property or method 'append'" -- and yes, I need this to run in Internet Explorer, since a lot of the PCs in my workplace primarily still use that browser. I tried running a polyfill from the MDN page, but this still doesn't work.

  4. #4
    Senior Coder coothead's Avatar
    Join Date
    Jan 2004
    Location
    chertsey, a small town 25 miles south west of london, england.
    Posts
    3,540
    Thanks
    3
    Thanked 637 Times in 623 Posts
    Hi there AIRey,

    I think that xelawho has just made a typo.

    This...

    Code:
    
        document.body.append(lnk)
    ...should really be...

    Code:
    
        document.body.appendChild(lnk);

    coothead
    ~ the original bald headed old fart ~

  5. #5
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Location
    Here
    Posts
    3,980
    Thanks
    58
    Thanked 710 Times in 705 Posts
    Oh, yes. Sorry about that. One browser doesn't support the append method and guess which one it is...

    https://developer.mozilla.org/en-US/..._compatibility

    Coothead is quite right. appendChild should do the trick.
    Last edited by xelawho; Aug 9th, 2019 at 03:11 PM.

  6. #6
    Senior Coder coothead's Avatar
    Join Date
    Jan 2004
    Location
    chertsey, a small town 25 miles south west of london, england.
    Posts
    3,540
    Thanks
    3
    Thanked 637 Times in 623 Posts

    Quote Originally Posted by xelawho View Post
    coothead is quite right.
    Well, not really.

    As JavaScript is not my forte, I was unaware of the existence of append()
    and wrongly assumed that it was a just a typo on your part.

    coothead
    ~ the original bald headed old fart ~

  7. #7
    New to the CF scene
    Join Date
    Aug 2019
    Location
    Indiana
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Quote Originally Posted by xelawho View Post
    Code:
             lnk.href = document.getElementById("myCanvas").toDataURL("image/png").replace(/^data:image\/[^;]/, 'data:application/octet-stream');
    The big issue I'm having with this now is that there doesn't seem to be any way to convert this application/octet-stream to something that can be read in C#. The Convert method is only able to do .FromBase64String() and .FromBase64CharArray() and so far I'm not finding any other way to pass the data through. What's even weirder is that the kind of data I get back from application/octet-stream is still a Base64 string, with one having a value of data:application/octet-streamng;base64,iVBORw0KGgoAAAANSU....

    If i just put imageData into bw.Write(), an image at least appears in the directory, but it's a blank image.

  8. #8
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Location
    Here
    Posts
    3,980
    Thanks
    58
    Thanked 710 Times in 705 Posts
    I guess that if it's working on the JS side it's a problem with your C#. I don't really know enough about C# to do any more than google it like you could, but there seem to be a few ways to write to a file. The simplest would seem to be

    Code:
    image = image.replace('data:image/png;base64,', '');
    on the JS side and

    Code:
    File.WriteAllBytes("test.png", imageData);
    on the C# side

    but like I say, that's about 98% guesswork

  9. #9
    New to the CF scene
    Join Date
    Aug 2019
    Location
    Indiana
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Apparently this was an issue with how the UploadImage was handling the Base64 data, and not necessarily an issue with how the image was written on the JavaScript side. Now instead of using a FileStream, I am writing the data with a byte[] array.
    Code:
    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();
    }
    Thanks to everyone who helped me out with this! Sorry that it wasn't as much of a JavaScript issue as I initially thought ^^;


 

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •