How to integrate a webcam with SPFx and store captured images in SharePoint document library

In this article, we will learn about how to integrate the react-webcam plugin in SPFx and store captured image in the SharePoint document library

Introduction
 
React WebCam is a 3rd party plugin for operating a webcam in React Projects
 
Open a command prompt. Create a directory for the SPFx solution.
md spfx-React-Webcam
 
Navigate to the above-created directory.
cd spfx-React-Webcam
 
Run the Yeoman SharePoint Generator to create the solution.
yo @microsoft/sharepoint
 
Solution Name
Hit Enter for a default name, (spfx-React-Webcam in this case), or type in any other name for your solution.
Selected choice - Hit Enter
 
Target for the component
Here, we can select the target environment where we are planning to deploy the client web part, i.e., SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice - SharePoint Online only (latest)
 
Place of files
We may choose to use the same folder or create a subfolder for our solution.
Selected choice - Same folder
 
Deployment option
Selecting Y will allow the app to be deployed instantly to all sites and be accessible everywhere.
Selected choice - N (install on each site explicitly)
 
Permissions to access web APIs
Choose if the components in the solution require permissions to access web APIs that are unique and not shared with other components in the tenant.
Selected choice - N (solution contains unique permissions)
 
Type of client-side component to create
We can choose to create a client-side web part or an extension. Choose the web part option.
Selected choice - WebPart
 
Web part name
Hit Enter to select the default name or type in any other name.
Selected choice - SpfxReactWebcam
 
Web part description
Hit Enter to select the default description or type in any other value.
 
Framework to use
Select any JavaScript framework to develop the component. Available choices are - No JavaScript Framework, React, and Knockout.
Selected choice - React
 
Yeoman generator will perform a scaffolding process to generate the solution. The scaffolding process will take a significant amount of time.
Once the scaffolding process is completed, lock down the version of project dependencies by running the below command: 
npm shrinkwrap
 
In the command prompt, type below command to open the solution in the code editor of your choice.
code .
 
NPM Packages Used,
On the command prompt, run below command.
  1. npm i @pnp/logging @pnp/common @pnp/odata @pnp/sp --save
for Pollyfills
  1. npm install --save @pnp/polyfill-ie11
 for react Webcam
  1. npm install react-webcam  
 Code Piece for Intialize Webcam
  1. import   Webcam from "react-webcam";  
  2.     <Webcam    
  3.           audio={false}    
  4.           height={550}    
  5.           ref={webcamRef}    
  6.           screenshotFormat="image/jpeg"    
  7.           width={550}    
  8.           videoConstraints={videoConstraints}    
  9.      />    
in SpfxReactWebcam.tsx
  1. import * as React from 'react';  
  2. /*import styles from './SpfxReactWebcam.module.scss';*/  
  3. import { ISpfxReactWebcamProps } from './ISpfxReactWebcamProps';  
  4. import { escape } from '@microsoft/sp-lodash-subset';  
  5. import {WebcamCapture} from './reactwebcam';  
  6. export default class SpfxReactWebcam extends React.Component<ISpfxReactWebcamProps, {}> {  
  7.   public render(): React.ReactElement<ISpfxReactWebcamProps> {  
  8.     return (  
  9.       <div>  
  10.         <WebcamCapture/>  
  11.       </div>  
  12.     );  
  13.   }  
  14. }  
in reactwebcam.tsx(Here i am using Hooks Concept)
  1. import * as React from "react";  
  2. import   Webcam from "react-webcam";  
  3. import { sp } from "@pnp/sp";  
  4. const videoConstraints = {  
  5.     width: 1280,  
  6.     height: 720,  
  7.     facingMode: "user"  
  8.   };  
  9.      
  10.   export const  WebcamCapture = () => {   
  11.     const [datacol, setdatacol] = React.useState();    
  12.     const webcamRef = React.useRef(null);  
  13.      
  14.     const capture = React.useCallback(  
  15.       () => {  
  16.         setdatacol(webcamRef.current.getScreenshot());  
  17.       },  
  18.       [webcamRef]  
  19.     );  
  20.     const remove = React.useCallback(  
  21.         () => {  
  22.           setdatacol("");  
  23.         },  
  24.         []  
  25.       );  
  26.        let Save= ()=>{  
  27.       var myblob=  b64ToBlob(datacol);  
  28.         sp.web.getFolderByServerRelativeUrl("/sites/SPCapabilityTeam/MadhanNC/PnpLibrary/").files.add("Madhan.jpg", myblob as Blob, true).then(output => {  
  29.   
  30.         });  
  31.      /*  
  32.      import { sp } from '@pnp/sp'; 
  33.  
  34. (async () => { 
  35.  
  36.   const folder = sp.web.getFolderByServerRelativeUrl('/sites/site/Shared Documents'); 
  37.   const { file, data } = await folder.files.add('new-one.txt', 'data'); 
  38.   const item = await file.getItem(`Id&_hash=${new Date().toISOString()}`); // force no-cashed response 
  39.   await item.update({ Title: new Date().toISOString() }); 
  40.  
  41. })().catch(console.warn); 
  42.  
  43. web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test/").files.add("abcd.txt", file, true).then(f => { 
  44.  
  45.     f.file.listItemAllFields.get(new ODataDefaultParser(), { 
  46.                 headers: { 
  47.                     "Cache-Control": "no-cache", 
  48.                 } 
  49.             }).then(function (item) { 
  50.                 item.update({ 
  51.                     Title: "Updated Title", 
  52.                     Description: "Updated desc" 
  53.                 }); 
  54.             }); 
  55.         });        
  56. }); 
  57.      */  
  58.   
  59.        };  
  60.      
  61.      
  62.     return (  
  63.       <>  
  64.         <Webcam  
  65.           audio={false}  
  66.           height={550}  
  67.           ref={webcamRef}  
  68.           screenshotFormat="image/jpeg"  
  69.           width={550}  
  70.           videoConstraints={videoConstraints}  
  71.             
  72.         />  
  73.          <div>   {datacol ? <img src={datacol} /> : null} </div>  
  74.         <button onClick={capture}>Capture photo</button>  
  75.         <button onClick={remove}>remove photo</button>  
  76.         <button onClick={Save}>Save photo</button>  
  77.       </>  
  78.     );  
  79.   };  
  80.   
  81.   export const b64ToBlob = (base64: string, type: string = 'application/octet-stream'): Blob => {  
  82.     const byteArray = Uint8Array.from(  
  83.       window.atob(base64.replace("data:image/jpeg;base64,",""))  
  84.         .split('')  
  85.         .map((char) => char.charCodeAt(0))  
  86.     );  
  87.     return new Blob([byteArray], { type });  
  88.   };  
  89.   export const b64ToArrayBuffer = (base64: string): ArrayBuffer => {  
  90.     const binaryString = window.atob(base64.replace("data:image/jpeg;base64,",""));  
  91.     const len = binaryString.length;  
  92.     const bytes = new Uint8Array(len);  
  93.     for (let i = 0; i < len; i++) {  
  94.       bytes[i] = binaryString.charCodeAt(i);  
  95.     }  
  96.     return bytes.buffer;  
  97.   };  
  98.     
 webcamRef.current.getScreenshot()  method gets the captured image value in the form of base64.
To upload a file to the sharepoint document library, we 
either need to convert it to blob, or arraybuffer. I've mentioned both methods above

The code for uploading an image:
  1. var myblob=  b64ToBlob(datacol);      
  2. sp.web.getFolderByServerRelativeUrl("/sites/SPCapabilityTeam/MadhanNC/PnpLibrary/").files.add("Madhan.jpg", myblob as Blob, true).then(output => {  });   
 or...
  1. var myarraybuffer=  b64ToArrayBuffer(datacol);  
  2. sp.web.getFolderByServerRelativeUrl("/sites/SPCapabilityTeam/MadhanNC/PnpLibrary/").files.add("Madhan.jpg", myarraybuffer as ArrayBuffer, true).then(output => {   });  
Here i am using PnpLibrary as my document library and SPcapabilityTeam/MadhanNC as the site
 
 
 
Here, the first image is a live image and the second one is a captured image. 
 
I hope that this helps someone. Happy Coding :)