【问题标题】:How do I upload images through AJAX after compressing the images?图片压缩后如何通过AJAX上传图片?
【发布时间】:2021-05-17 19:15:00
【问题描述】:

我有一个允许上传多个文件的界面。用户在文件元素上转储一组文件。 然后脚本设置这些图像的高度、宽度和 dpi 并以大缩略图显示。

一切正常。如果我右键单击并保存图像,它现在是 1600x1600,96 dpi。正是我指定的。

现在,我需要通过 AJAX 创建一个新的 FileList 来上传图像。但是如何指定文件列表的文件名和内容呢?

我有一个隐藏文件元素,我将使用新文件列表更新它,这将触发 AJAX。

这是 HTML 和 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>

我基本上被困在如何用正确的内容填充新的 FileList —— 压缩图像及其文件名。

【问题讨论】:

  • 确保您的问题可重现和明确识别会很有用。不是这种情况。另外:1) $("#imgSave").on("change" 并且没有 #imgSave 元素...或者它是 #saveImages?那将是一个错字。 2) 同类:const data = new FormData($("#frmSaveImage")[0]);,其中没有#frmSaveImage,而是#frmSaveImages(以s 结尾)... 另一个错字。 --- 我的建议是打开console 并查找错误。在发布之前解决您可以解决的问题。
  • 那里。我为你简化了。此时只需一个按钮即可构建新的表单数据。

标签: javascript jquery ajax upload


【解决方案1】:

我昨天终于有时间回到这个话题了。这有效:

<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】:

    您遇到了一个小问题,导致 nothing 起作用:当绝对没有元素具有该类时,循环遍历 .newCanvas 元素。所以循环完全没有运行。

    还有一些可以做的“改进”...但是要填充formData,这是唯一的事情。

    所以我在该行添加了class="newCanvas"&lt;canvas id="canvas_${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>`
    );
    

    请注意,我还添加了 data-name 来保存文件名。该信息必须由canvas 携带,以便稍后在.each() 循环中使用。

    现在进行一些改进:

    • 你到处使用反引号。好的做法是在真正需要时使用它们,例如当某些模板 ${variable} 在字符串中时...

    • document.getElementById($(this).attr(id))this 完全相同。
      除了您在this 上进行了jQuery 查找以获取id,然后使用id 使用普通JS 检索元素。
      .each() 循环中,this 是当前循环的元素。

    • .toBlob() 回调中,$(this) 不再是canvas 元素。在调用.toBlob()之前,您必须从画布中获取信息

    • 您使用setTimeout() 等待canvas.toblob() 结果...这不可靠。我建议您使用 formData 中附加文件的数量,并将其与要处理的 .newCanvas 元素的数量进行比较。
      该比较必须在 canvas.toblob() 回调中。

    所以这是我建议你尝试的代码。

    $(`#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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多