{"id":67452,"date":"2016-08-25T14:10:39","date_gmt":"2016-08-25T14:10:39","guid":{"rendered":"https:\/\/www.simple-talk.com\/?p=67452"},"modified":"2022-05-02T21:05:42","modified_gmt":"2022-05-02T21:05:42","slug":"taking-pictures-html","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/javascript\/taking-pictures-html\/","title":{"rendered":"Taking Pictures from HTML"},"content":{"rendered":"<p>Web developers generally get asked by friends to write them a plain simple web site for their own personal business. \u201cIt doesn\u2019t have to be a truly professional thing,\u201d they usually say while pleading you for help, \u201cit just has to let me track my business \u00a0activities.\u201d This happened to me recently when a close friend of mine asked for a small web site that would enable her to stop having to record work tasks on her paper agenda. No big deal, I thought. \u00a0I tried to tempt her with Google calendar and mobile apps, but she wanted to do it the way she\u2019d grown used to. After a busy weekend, I served her a working prototype that had a bunch of HTML forms and a couple of SQL Server tables. She was so happy and started using the site immediately. Guess what? As expected, I was then swamped with a long list of small changes that \u2018shouldn\u2019t take much to do\u2019 but would make the site much more enjoyable for her to use. One idea in particular intrigued me, and initially scared me a bit, I\u2019ll admit. She wanted to take pictures and associate these with a work task.<\/p>\n<p>Here\u2019s how my friend described the use case: \u201cWhen I\u2019m busy doing a task, I might want to take a picture of what I\u2019m doing, even when all I have with me is a smartphone. Can you modify the web site so that I can take a picture with the smartphone\u2019s camera?\u201d<\/p>\n<p>I was about to say to her that it was impossible to do this from a web site page, and that she needed an ad-hoc mobile app for that. Then I paused for thought.\u00a0 Maybe you don\u2019t need to use one of the \u00a0HTML5-based frameworks that \u00a0are used to build native apps. Then it hit me: \u00a0Of course you can take pictures from within a web page, either through the camera or the webcam of a mobile phone, a tablet or even a laptop. This article explains how to do it with \u00a0JavaScript and HTML. The final product is a plain HTML page that you visit using a standard browser on a mobile phone, a tablet or even a laptop.<\/p>\n<h1>The Media Capture specification<\/h1>\n<p>Back in 2014, the W3C issued a Media Capture specification to define the way that the HTML INPUT file element can be extended to access any media capture mechanisms that were available on a particular device. This device could be the camera, camcorder, microphone or just the library of media files. The Media Capture specification is defined here : <a href=\"http:\/\/www.w3.org\/TR\/html-media-capture\/\">http:\/\/www.w3.org\/TR\/html-media-capture\/<\/a>.<\/p>\n<p>According to the specification, the INPUT file element features an additional attribute named \u2018<strong>capture<\/strong>\u2019. The recommended syntax is shown below:<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">&lt;input type=\"file\" name=\"uploader\" id=\"uploader\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0 accept=\"image\/*\" \r\n\u00a0\u00a0 capture \r\n\/&gt;<\/pre>\n<p>The <strong>accept <\/strong>attribute is not strictly related to the Media Capture specification. You can use it to simply suggest to the browser which files it has to show if you browse for a file to upload. Note also that the attribute is not widely supported by browsers, especially within \u00a0the mobile segment. For more information on the <strong>accept<\/strong> attribute you can check <a href=\"http:\/\/caniuse.com\/#feat=input-file-accept\">http:\/\/caniuse.com\/#feat=input-file-accept<\/a>.<\/p>\n<p>The most relevant part of the Media Capture specification is the <strong>capture<\/strong> attribute. According to the specification, the attribute is a Boolean value that indicates the preferred mechanism for picking the file to upload. If you set the attribute, then you\u2019re telling the browser to use one of the capture mechanisms the device has available, instead of the file system.<\/p>\n<p>This is the only the theory, however.<\/p>\n<p>In practice, the net effect of the above HTML snippet changes depending on the browser and the form factor of the device (mobile, desktop). Furthermore, you\u2019ll be surprised to see that, in practice, mobile browsers mostly ignore the<strong> capture<\/strong> attribute and pay greater account to the value of the<strong> accept<\/strong> attribute.<\/p>\n<h1>Taking Browsers to Capture Media Content<\/h1>\n<p>Let\u2019s see what some browsers do when processing the HTML file input element with the capture attribute.<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">&lt;input type=\"file\" name=\"uploader\" id=\"uploader\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0 accept=\"image\/*\" \r\n\u00a0\u00a0 capture \r\n\/&gt;<\/pre>\n<p>Desktop browsers always show the <strong>Browse<\/strong> dialog box and use the value of the<strong> accept<\/strong> attribute to offer a filtered list of files. At present, desktop browsers completely ignore the <strong>capture<\/strong> element. Things are slightly different on the mobile side. You can find plenty of examples and posts discussing a slightly older Media Capture specification that uses \u00a0the syntax below:<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">&lt;input type=\"file\" name=\"uploader\" id=\"uploader\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n\u00a0\u00a0 accept=\"image\/*\" \r\n\u00a0\u00a0 capture=\"camera\"\r\n\/&gt;<\/pre>\n<p>As you can see, the <strong>capture<\/strong> element seems now to accept a string instead of a Boolean value. Feasible values for the <strong>capture<\/strong> attribute seem to be \u2018<strong>microphone<\/strong>\u2019, <strong>\u2018camcorder\u2019<\/strong> and \u2018<strong>camera<\/strong>\u2019.<\/p>\n<p>Both iOS 6 and Android 4\u2014the oldest versions of the most popular mobile operating systems supporting file upload\u2014completely ignore the capture attribute. At present, both the above code snippet and the code snippet below produce the same output.<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">&lt;input type=\"file\" name=\"uploader\" id=\"uploader\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0 accept=\"image\/*\" \r\n\/&gt;<\/pre>\n<p>Only the value of the<strong> accept<\/strong> attribute is processed, and browsers use it to decide which options to offer to the actual user. Figure 1 shows the effect of the code snippet on a recent iPhone device. The user clicks the camera button to add a picture to the currently displayed record and the iOS device responds as shown. The screenshot is taken from a device equipped with iOS9.<\/p>\n<p><strong><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-67455\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/08\/fig1.png\" alt=\"fig1\" width=\"329\" height=\"585\" \/><\/strong><\/p>\n<p class=\"caption\"><strong>F<\/strong><strong>IGURE 1. The HTML Input Element on iOS 6+ devices<\/strong><\/p>\n<p>In Figure 2 a sample HTML page shows what happens when you view it with an Android device.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-67456\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/08\/fig2.png\" alt=\"fig2\" width=\"329\" height=\"585\" \/><\/p>\n<p class=\"caption\"><strong>FIGURE 2. The HTML Input Element on Android 4+ devices<\/strong><\/p>\n<p>My friend wanted to use the camera to snap new pictures to upload to the web site. This was only relevant for a mobile device so the camera button had to appear on the website only when accessed from the mobile. This was one of those weird situations in which it wasn\u2019t enough to use Bootstrap or responsive design to hide the camera button on the desktop. To make sure the Camera button only appears on mobile devices running on at least iOS 6 or Android 4, you need a specific device detection library. For the purpose, I used the community edition of WURFL.JS. (For more information, check out <a href=\"http:\/\/web.wurfl.io\">http:\/\/web.wurfl.io<\/a>.)<\/p>\n<p>The markup discussed so far, \u00a0the old familiar INPUT file element with the extra accept attribute, serves well the purpose of letting mobile users to upload a picture that is already saved to the local gallery or to snap a new one. In fact, by selecting the Camera option (See Figure 2) the device will switch to the camera to let users tap and save a new photo.<\/p>\n<p>This is only part of the work required, though, for a realistic web page. The process of selecting an existing photo or taking a new one is only the first part of a larger slice of work. Having got the image, how would you then upload the image to the server and store it?<\/p>\n<h1>Uploading the File to the Server<\/h1>\n<p>For an expert ASP.NET developer, it is no big deal to upload an image to the web server using the INPUT element. The browser does most of the work required on the client side. On the server side itis sufficient to arrange a controller endpoint that can accept a parameter of type <strong>HttpPostedFileBase<\/strong>, as below.<\/p>\n<pre class=\"theme:vs2012 lang:asp decode:true \">[HttpPost]\r\npublic string AddPhoto(HttpPostedFileBase selectedPicture) { ... }<\/pre>\n<p>Initially, I coded the file-selection and upload tasks in the traditional way. This involved placing the INPUT file element in a HTML form and letting the submit button of the form do the hard work of serializing the form\u2019s data and uploading it via the browser. It worked fairly well as long as I tested it on the desktop. When I moved it to a staging web server and accessed the pages with a mobile device I noticed a couple of interesting things.<\/p>\n<ul>\n<li>First, submitting a form via the browser in the standard way is not a pleasant experience for the user: Not that this really comes as a surprise, but especially on an average mobile phone is can be painfully slow.<\/li>\n<li>Second, when you take a picture with a mobile device the resulting picture is typically quite large as most devices these days have amazing cameras.<\/li>\n<\/ul>\n<p>The combination of these two factors made for a typical unacceptably long upload time with nearly no feedback for the user. My friend didn\u2019t like it and asked for some improvement.<\/p>\n<p>The primary improvement to the upload procedure is to change it to \u00a0use<strong> XmlHttpRequest <\/strong>and <strong>FormData<\/strong>. You programmatically build up an HTML form using the<strong> FormData<\/strong> object and, when done, pass it to <strong>XmlHttpRequest<\/strong> to post to the remote URL. The server side of the code remains unchanged: a controller endpoint accepting a <strong>HttpPostedFileBase<\/strong> object.<\/p>\n<p>To hook up into the upload client side procedure, you define a JavaScript handler for the<strong> onchange<\/strong> event of the<strong> INPUT <\/strong>file element.<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">&lt;input type=\"file\" name=\"selectedPicture\" id=\"selectedPicture\" \r\n\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0accept=\"image\/*\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 onchange=\"pictureSelected()\" \/&gt;<\/pre>\n<p>The <strong>onchange <\/strong>event fires every time a file is selected from either the browse dialog or the device gallery, or if it is taken with the camera. Here\u2019s the skeleton of such an <strong>onchange<\/strong> event handler:<\/p>\n<pre class=\"theme:vs2012 lang:c# decode:true \">function pictureSelected() {\r\n\u00a0\u00a0\u00a0 var filename = document.getElementById('selectedPicture').files[0];\r\n\u00a0\u00a0\u00a0 :\r\n}<\/pre>\n<p>Retrieving the file name is an easy step. The code above shows how it could work with the DOM API. All files selected through the <strong>INPUT <\/strong>element are gathered in the files collection of the underlying DOM object. If you enabled multiple file selection on the <strong>INPUT <\/strong>element (by adding the multiple attribute) then all selected file names are made available through the <strong>files<\/strong> collection.<\/p>\n<p>Once the names of the selected files are known, you have to find a way to upload their bits to the web server. There are a number of emerging API to read the content of local files through JavaScript. An interesting one is <strong>FileReader<\/strong> that you find described here <a href=\"https:\/\/developer.mozilla.org\/en\/docs\/Web\/API\/FileReader\">https:\/\/developer.mozilla.org\/en\/docs\/Web\/API\/FileReader<\/a>. As with any relatively new web APIs, <strong>FileReader<\/strong> aims at making development super easy but at least initially browser support may be painful. Unless you can exercise a strict control over the browsers being used, I wouldn\u2019t suggest you go for it now. Given my very particular requirements\u2014a web site for a single user\u2014it was definitely an option: \u00a0I even made some tests with <strong>FileReader<\/strong>. In the end, though, I opted for a more traditional solution: filling up a form and posting via <strong>XmlHttpRequest.<\/strong> Here\u2019s how.<\/p>\n<pre class=\"theme:vs2012 lang:c# decode:true \">var fd = new FormData();\r\nfd.append(\"file\", document.getElementById('camera').files[0]);\r\nfd.append(\"taskId\", @Model.TaskId);\r\n\r\n\/\/ Add more data to the form (if required)\r\n:\r\nvar xhr = new XMLHttpRequest(); \r\nif (xhr.upload) {\r\n\u00a0\u00a0 \/\/ Use the ad hoc uploader to support\r\n\u00a0\u00a0 \/\/ advanced features such as cancel\/progress\r\n}<\/pre>\n<p>If defined, the upload property indicates the<strong> XmlHttpRequest <\/strong>ability to support advanced monitoring features, such as a progress bar.<\/p>\n<pre class=\"theme:vs2012 lang:c# decode:true \">xhr.upload.addEventListener(\"progress\", progress, false);\r\nxhr.addEventListener(\"load\", complete, false);\r\nxhr.addEventListener(\"abort\", canceled, false);<\/pre>\n<p>The following code registers an event handler for reporting back the progress made on the upload, and to handle completion of the upload. Finally, you can even implement a cancel feature. Next, you simply start the Ajax post to the web server endpoint.<\/p>\n<pre class=\"theme:vs2012 lang:c# decode:true \">xhr.open(\"POST\", url);<\/pre>\n<p>Note that <strong>XmlHttpRequest<\/strong> may support progress events for both download and upload operation. However, download events fire on the <strong>XmlHttpRequest<\/strong> object itself whereas upload notifications are fired on the upload child object.<\/p>\n<h1>Refreshing the user interface<\/h1>\n<p>Figure 3 presents a screenshot of the upload feature of the sample application.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-67454\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/08\/fig3.png\" alt=\"fig3\" width=\"329\" height=\"585\" \/><\/p>\n<p class=\"caption\"><strong>FIGURE 3. Monitoring the upload of a photo after taking it<\/strong><\/p>\n<p>One more tip for the user interface is to keep the actual user interface of the HTML <strong>INPUT<\/strong> element hidden and use a plain button to control it programmatically. In Figure 3, you can see a button with a camera icon. It\u2019s a plain Bootstrap-styled client button. In the same page, hidden from view, there\u2019s the <strong>INPUT<\/strong> file element. When the camera button is clicked, it simply passes the event on to the <strong>INPUT<\/strong> element.<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">&lt;button type=\"button\" class=\"btn btn-primary\" onclick=\"takePicture()\"&gt;\r\n\u00a0\u00a0\u00a0 &lt;i class=\"fa fa-camera\"&gt;&lt;\/i&gt;\r\n&lt;\/button&gt;\r\n&lt;input type=\"file\" name=\"selectedPicture\" id=\"selectedPicture\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 style=\"display:none;\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 accept=\"image\/*\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 onchange=\"pictureSelected()\" \/&gt;<\/pre>\n<p>The click-handler of the button looks like below:<\/p>\n<pre class=\"theme:vs2012 lang:xhtml decode:true \">function takePicture() {\r\n\u00a0\u00a0 $(\"#selectedPicture\").click();\r\n}<\/pre>\n<p>\u00a0\u00a0You have an extra HTML element around, but the final effect on the user is much nicer.<\/p>\n<h1>Summary<\/h1>\n<p>When my friend wanted an application that allowed her to add \u00a0iPhone picture from within a mobile web site, I admit that it looked like a very hard challenge to take on. In the end, though, I not I implemented the requested feature but I enjoyed doing it too. It led me to discover many features of the HTML DOM and <strong>XmlHttpRequest<\/strong> that I didn\u2019t know about. The lesson I learned is that you don\u2019t need a special mobile app to take pictures with an iPhone or Android device and upload them to a server endpoint: you can do it all in the HTML5 and ASP.NET that you\u2019re familiar with. Yeah, and SQL Server too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes a request from a user who doesn&#8217;t appreciate the limitations of the technology can jolt you into discovering that an application feature that was, until recently, difficult to achieve  is suddenly relatively easy.  Dino was asked to allow the user to take photographs and associate  them with an item of work. After he&#8217;d recovered from the shock, he decided that it was achievable, and now describes how he went on and did it.&hellip;<\/p>\n","protected":false},"author":221911,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[146044],"tags":[],"coauthors":[6780],"class_list":["post-67452","post","type-post","status-publish","format-standard","hentry","category-javascript"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/67452","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221911"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=67452"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/67452\/revisions"}],"predecessor-version":[{"id":91036,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/67452\/revisions\/91036"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=67452"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=67452"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=67452"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=67452"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}