skip to Main Content

I have an interface that allows multiple file uploads. The user dumps a set of files on the file element.
The script then sets the height, width and dpi of those images and displays them in a large thumbnail.

That all works fine. If I right-click and save an image, it is now 1600×1600 at 96 dpi. Exactly what I specified.

Now, I need to upload the images through AJAX by creating a new FileList. But how do I specify the filename and the content for the file list?

I have a hidden file element which I’ll update with the new file list which will trigger the AJAX.

Here is the HTML and the javascript/jquery:

<form enctype="multipart/form-data" id="frmUploadImage">
    <input name="files" type="file" id="imgUpload" class="upload" multiple accept="image/*"/>
</form>
<form enctype="multipart/form-data" id="frmSaveImage" style="display:none">
    <input name="files" type="file" id="saveImages" class="upload" multiple accept="image/*"/>
</form>
<input type="button" id="saveIt" value="Save">

<script>
    $(`#imgUpload`).on(`change`, function() {
        const $fileUpload = $(this);
        Array.from($fileUpload.get(0).files).forEach(function(f, i) {
            $(`#compressResultsContainer`).append(`<div id="image_${i}_container" class="compressedImageContainer"><img id="source_${i}" style="display:none"/><img id="image_${i}" class="compressedImage" style="width: 200px; height:200px"/><canvas id="canvas_${i}" style="display:none"></canvas><div>`);
            let $preview = $(`#source_${i}`);
            var reader = new FileReader();
            reader.addEventListener(`load`, function(e) {
                $preview.attr(`src`, e.target.result);
                $preview.on(`load`, function() {
                    console.log(`loading preview ${i}`);
                    compressFile(this, i);
                });
            }, false);
            if (f) {
                reader.readAsDataURL(f);
            }
        });
    });

    function compressFile(loadedData, imgNum) {
        var mime_type = `image/jpeg`;
        var cvs = document.getElementById(`canvas_${imgNum}`);
        cvs.width = 1600;
        cvs.height = 1600;
        var ctx = cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 1600,1600);
        var newImageData = cvs.toDataURL(mime_type, 96);
        var result_image_obj = new Image();
        result_image_obj.src = newImageData;
        $(`#image_${imgNum}`).attr(`src`, result_image_obj.src);
    }


// my test function just to see if I can build new form data---
$(`#saveIt`).on(`click`, function() {
    let formData = new FormData();
    $(`.newCanvas`).each(function() {
        let file;
        const canvas =document.getElementById($(this).attr(`id`));
        canvas.toBlob((blob) => {
            console.log(`canvas:`, $(this).attr(`id`));
            file = new File([blob], $(this).attr(`filename`), { type: `image/jpeg` });
            formData.append(`savedImages`, new File([blob], $(this).attr(`filename`), { type: `image/jpeg` }));
            console.log(file);
        }, `image/jpeg`);

    });
    setTimeout(function() { console.log(formData); }, 3000);
});
</script>

I’m basically stuck on how to populate the new FileList with the proper content — the compressed images and their filename.

2

Answers


  1. Chosen as BEST ANSWER

    I finally had time to get back to this yesterday. This works:

    <h1>Image Maintenance</h1><br>
    <input name="files" type="file" id="imgProcess" class="upload" multiple accept="image/*"/>
    <div id="progressContainer">
        <div id="processProgressContainer">
            <progress id="processProgress" value="0" style="vertical-align: middle;"></progress>
            <span id="processStatus" style="margin-left: 20px;"></span>
        </div>
        <div id="uploadProgressContainer" style="display:none">
            <span style="width: 150px; float:left">Seasonal  Upload</span>
            <span style="width: 150px; float:left">.Com Upload</span>
            <span style="width: 150px; float:left">Repository Upload</span><br style="clear:both"/>
            <span style="width: 150px; float:left"><progress id="seasonalProgress" value="0" style="vertical-align: middle;"></progress></span>
            <span style="width: 150px; float:left"><progress id="comProgress" value="0" style="vertical-align: middle;"></progress></span>
            <span style="width: 150px; float:left"><progress id="repositoryProgress" value="0" style="vertical-align: middle;"></progress></span>
        </div>
    </div>
    <input type="button" id="saveIt" value="Save">
    <input type='text' class='chromeAutoInputCatcher' tabindex='-1' name='stupidChromeInputCatcher'></input>
    <div id="compressResultsContainer"></div>
    
    <script>
    // @ts-nocheck
    /* eslint-disable no-undef */
    /* eslint-disable max-len */
    /* eslint-disable no-invalid-this */
    /* eslint-disable require-jsdoc */
    /* jshint esversion: 8 */
    
    let kpData;
    let repositoryData;
    let collection = `524`;
    let skuString = `,`;
    $(`#imgProcess`).on(`change`, function() {
        $(`#uploadProgressContainer`).hide();
        $(`#processProgressContainer`).show();
        $(`#processProgress`).val(0);
        kpData = new FormData();
        repositoryData = new FormData();
        let rootsku=``;
        skuString = `,`;
        $(`#compressResultsContainer`).html(``);
        //at some point, let's freeze all input until we get done compressing and displaying
        const $fileUpload = $(this);
        const numPics = $fileUpload.get(0).files.length;
        $(`#processProgress`).attr(`max`, numPics);
        Array.from($fileUpload.get(0).files).every(f=> {
            const thisSku = f.name.split(`-`).slice(0, 4).join(`-`);
            if (!rootsku.length) {
                rootsku = thisSku.split(`-`)[0];
            }
            const thisrootsku = thisSku.split(`-`)[0];
            if (rootsku !== thisrootsku) {
                alert(`found multiple root skus. Aborting`);
                $(`#imgProcess`).val(``);
                kpData = new FormData();
                repositoryData = new FormData();
                return false;
            }
            return true;
        });
        Array.from($fileUpload.get(0).files).forEach(function(f, i) {
            const thisSku = f.name.split(`-`).slice(0, 4).join(`-`);
            if (skuString.indexOf(thisSku) < 0) {
                skuString += `${thisSku},`;
            }
    
            $(`#compressResultsContainer`).append(`<div id="image_${i}_container" class="compressedImageContainer"><img id="source_${i}" style="display:none"/><img id="image_${i}" filename="${f.name}" class="compressedImage" style="width: 200px; height:200px; float:left"/><canvas class="newCanvas" id="canvas_${i}" style="display:none" filename="${f.name}"></canvas><span style="float:left">${f.name}</span></div><br style="clear:both"/>`);
            let $preview = $(`#source_${i}`);
            var reader = new FileReader();
            reader.addEventListener(`load`, function(e) {
                $preview.attr(`src`, e.target.result);
                $preview.on(`load`, function() {
                    createImages(this, i);
                    $(`#processProgress`).val($(`#processProgress`).val() + 1);
                    $(`#processStatus`).html(`${$(`#processProgress`).val()} of ${numPics} file: ${f.name} `).attr(`filenum`, $(`#processProgress`).val());
                    if ($(`#processProgress`).val() === numPics) {
                        getSkus(rootsku);
                        console.log(`done processing`);                 
                    }
                });
            }, false);
            if (f) {
                reader.readAsDataURL(f);
            }
        });
    });
    
    function createImages(loadedData, imgNum) {
        var mime_type = `image/jpeg`;
        var cvs = document.getElementById(`canvas_${imgNum}`);
        var cvsName = cvs.getAttribute(`filename`);
        //Create the detail version of the image
        cvs.width = 800;
        cvs.height = 800;
        cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 800, 800);
        //display the image
        var newImageData = cvs.toDataURL(mime_type);
        var result_image_obj = new Image();
        result_image_obj.src = newImageData;
        $(`#image_${imgNum}`).attr(`src`, result_image_obj.src);
        //convert the canvase to an uploadable file and append it to our form data
        cvs.toBlob((blob) => {
            let file = new File([blob], cvsName, { type: `image/jpeg` });
            kpData.append(`detail`, file, cvsName);
        }, `image/jpeg`,0.96);
    
        //create the general image
        cvs.width = 370;
        cvs.height = 370;
        cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 370, 370);
        cvs.toDataURL(mime_type);
        cvs.toBlob((blob) => {
            let file = new File([blob], cvsName, { type: `image/jpeg` });
            kpData.append(`general`, file, cvsName);
        }, `image/jpeg`,0.96);
    
        //create the thumbnail
        cvs.width = 240;
        cvs.height = 240;
        cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 240, 240);
        cvs.toDataURL(mime_type);
        cvs.toBlob((blob) => {
            let file = new File([blob], cvsName, { type: `image/jpeg` });
            kpData.append(`thumb`, file, cvsName);
        }, `image/jpeg`,0.96);
    
        //create the repository image for Amazon, Zulilly, Zappos and our wholesale customers. Zullily has the greatest minimum requirements so we'll use those for everyone
        cvs.width = 1600;
        cvs.height = 1600;
        cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 1600, 1600);
        cvs.toDataURL(mime_type);
        cvs.toBlob((blob) => {
            let file = new File([blob], cvsName, { type: `image/jpeg` });
            repositoryData.append(`imgfiles`, file, cvsName);
        }, `image/jpeg`, 1);
    }
    
    
    let uploadLocation = `/${scPcFolder}/pc/catalog/`;
    saveImages = function(url, formData,server) {
        data = formData;
        $(`#${server}Progress`).val(0);
        $.ajax({
            url: url,
            cache: false,
            contentType: false,
            processData: false,
            enctype: `multipart/form-data`,
            data: data,
            type: `POST`,
            xhr: function() {
                var myXhr = $.ajaxSettings.xhr();
                if (myXhr.upload) {
                    myXhr.upload.addEventListener(`progress`, function(e) {
                        if (e.lengthComputable) {
                            $(`#${server}Progress`).attr({
                                value: e.loaded,
                                max: e.total,
                            });
                        }
                    }, false);
                }
                return myXhr;
            }
        }).done(function(data) {
        }).fail(function(xhr, textStatus, errorThrown) {
            console.log(xhr, textStatus, errorThrown);
        }).always(function() {
        });
    };
    
    
    function parseReturn(data = ``, nonJson = { beforeData: ``, afterData: `` }) {
        data = data.replace(new RegExp(`r?n`, `g`), ``).replace(/t/g, ``);//.replace(/'/g, `''`);
        nonJson.beforeData = data.substring(0, data.indexOf(`{`));
        nonJson.afterData = data.substring(data.lastIndexOf(`}`) + 1, 99999999);
        data = data.substring(data.indexOf(`{`), data.lastIndexOf(`}`) + 1);
        try {
            return JSON.parse(data);
        } catch (e) {
            console.log(`error: `, e);
            console.log(data);
        }
    }
    
    $(`#saveIt`).on(`click`, function() {
        $(`#processProgressContainer`).hide();
        $(`#uploadProgressContainer`).show();
        saveImages(`ImageManagement_AJAX.asp?action=saveFiles&uploadLocation=${uploadLocation}&skuString=${skuString}&collection=${collection}`, kpData,`seasonal`);
        saveImages(`https://example.com/uploadify/ImageManagement_AJAX.asp?action=saveFiles&uploadLocation=${uploadLocation}`, kpData,`com`);
        saveImages(`https://example.com/Image/Upload`, repositoryData,`repository`);
    });
    
    function getSkus(rootsku) {
        $.ajax({
            url: `ImageManagement_AJAX.asp`,
            data: { action: `getSkus`, rootsku: rootsku, collection: collection },
            type: `POST`,
        }).done(function(data) {
            //console.log(`data`, data);
            let objSkus = parseReturn(data);
    
            objSkus.skus.forEach(function(s) {
                s.sku=s.sku.split(`-`).slice(0, s.sku.split(`-`).length - 1).join(`-`);
            });
            let uniqueSkus = [...new Set(objSkus.skus.map((sku) => sku.sku))];
            let html=``;
            //make sure every sku has a primary image in this batch of files
            uniqueSkus.forEach(function(s) {
                if (!$(`.compressedImage[filename="${s}.jpg"]`).length) {
                    html += `<span style="float:left">${s}</span><span style="float:left; min-width:10px">&nbsp;</span>`;
                    console.log(`missing file: `, s);
                }
            });
            if (!html.length) {
                html = `Success`;
            } else {
                html += `<br style="clear:both"/><br/>`;
            }
            $(`#processStatus`).html(html);
            $(`#imgProcess`).val(``);
        }).fail(function(xhr, textStatus, errorThrown) {
            console.log(xhr, textStatus, errorThrown);
        }).always(function() {
        });
    }
    </script>
    

  2. You had ONE tiny issue which made nothing to work: looping through the .newCanvas elements when absolutely no element had the class. So the loop squarely didn’t run.

    There also is a couple "improvements" that can be done… But to have the formData populated, that was the only thing.

    So I added class="newCanvas" to <canvas id="canvas_${i}" on that line:

    $(`#compressResultsContainer`).append(
      `<div id="image_${i}_container" class="compressedImageContainer"><img id="source_${i}" style="display:none"/><img id="image_${i}" class="compressedImage" style="width: 200px; height:200px"/><canvas id="canvas_${i}" class="newCanvas" data-name="${f.name}" style="display:none"></canvas><div>`
    );
    

    Notice that I also added a data-name to hold the file name. That information has to be carried by the canvas to later be usable in the .each() loop.

    Now about a couple improvements:

    • You use backticks everywhere. Good practice would be to use them when really needed, like when some templating ${variable} is inside the string…

    • document.getElementById($(this).attr(id)) is EXACTLY the same as just this.
      Except that you’ve made a jQuery lookup on this to get the id and then used that id to retreive the element using plain JS.
      In an .each() loop, this is the element of the current loop.

    • Within the .toBlob() callback, $(this) no longer is the canvas element. You have to get the informations from the canvas before calling .toBlob()

    • You used a setTimeout() to wait for the canvas.toblob() results… That is not reliable. I suggest you to use the number of appended files in the formData and compare that with the number of .newCanvas element to process.
      That comparison has to be in the canvas.toblob() callback.

    So here is the code I suggest you to try.

    $(`#imgUpload`).on(`change`, function () {
      const $fileUpload = $(this);
      Array.from($fileUpload.get(0).files).forEach(function (f, i) {
        $(`#compressResultsContainer`).append(
          `<div id="image_${i}_container" class="compressedImageContainer"><img id="source_${i}" style="display:none"/><img id="image_${i}" class="compressedImage" style="width: 200px; height:200px"/><canvas id="canvas_${i}" class="newCanvas" data-name="${f.name}" style="display:none"></canvas><div>`
        );
        let $preview = $(`#source_${i}`);
        var reader = new FileReader();
        reader.addEventListener(
          `load`,
          function (e) {
            $preview.attr(`src`, e.target.result);
            $preview.on(`load`, function () {
              console.log(`loading preview ${i}`);
              compressFile(this, i);
            });
          },
          false
        );
        if (f) {
          reader.readAsDataURL(f);
        }
      });
    });
    
    function compressFile(loadedData, imgNum) {
      var mime_type = `image/jpeg`;
      var cvs = document.getElementById(`canvas_${imgNum}`);
      cvs.width = 1600;
      cvs.height = 1600;
      var ctx = cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 1600, 1600);
      var newImageData = cvs.toDataURL(mime_type, 96);
      var result_image_obj = new Image();
      result_image_obj.src = newImageData;
      $(`#image_${imgNum}`).attr(`src`, result_image_obj.src);
    }
    
    // my test function just to see if I can build new form data---
    $(`#saveIt`).on(`click`, function () {
      
      let allCanvas = $(`.newCanvas`)
      let canvasCount = allCanvas.length
      let formData = new FormData();
      
      allCanvas.each(function () {
        let file;
        const canvas = this   // document.getElementById($(this).attr(`id`));
        
        // Get the filename here
        // Because inside the .toBlob callback this and $(this) no longer is the canvas 
        let canvasName = this.getAttribute("data-name")
        
        canvas.toBlob((blob) => {
          console.log(`canvas:`, $(this).attr(`id`));
          file = new File([blob], canvasName, { type: `image/jpeg` });
          formData.append(
            `savedImages`,
            file
          );
          console.log(file);
          
          // Did we procces all canvas?
          // This part is to replce your setTimeout
          // So here you can call the Ajax
          if(formData.getAll("savedImages").length === canvasCount){
            
            // Just formData will print the constructor
            //console.log(formData);
            
            console.log(formData.getAll("savedImages"));
          }
        }, `image/jpeg`);
      });
      
      /*
      setTimeout(function () {
        console.log(formData);
      }, 3000);
      */
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <form enctype="multipart/form-data" id="frmUploadImage">
      <input name="files" type="file" id="imgUpload" class="upload" multiple accept="image/*" />
    </form>
    <form enctype="multipart/form-data" id="frmSaveImage" style="display:none">
      <input name="files" type="file" id="saveImages" class="upload" multiple accept="image/*" />
    </form>
    <input type="button" id="saveIt" value="Save">
    
    <div id="compressResultsContainer"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search