Maison >interface Web >js tutoriel >Comment utiliser la fonction de compression et de téléchargement d'images frontales mobiles ?

Comment utiliser la fonction de compression et de téléchargement d'images frontales mobiles ?

亚连
亚连original
2018-06-22 15:51:591230parcourir

L'éditeur ci-dessous partagera avec vous un exemple de compression et de téléchargement d'images front-end mobiles. Il a une très bonne valeur de référence et j'espère qu'il sera utile à tout le monde. Laissez l'éditeur venir jeter un œil.

Résumé : Je travaillais auparavant sur un petit projet de plateforme de jeu. Il y avait un module "User Center", qui. impliquait le téléchargement d'avatar. Lors du téléchargement de photos sur le terminal mobile, toutes les photos téléchargées sont des photos locales du téléphone mobile, et les photos locales sont généralement relativement volumineuses. En prenant comme exemple les smartphones actuels, de nombreuses photos prises ont généralement une taille de deux à trois mégaoctets. directement comme ceci, l'image sera trop grande. Si l'utilisateur utilise des données mobiles, télécharger l'image dans son intégralité n'est évidemment pas une bonne idée. Par conséquent, il est nécessaire d'effectuer une compression avant de télécharger. Après avoir recherché de nombreuses informations sur Internet, j'ai essayé de nombreuses méthodes et rencontré de nombreux pièges. Par exemple, Android peut compresser et télécharger des images avec succès, mais il ne peut pas être téléchargé sur iOS. Il a fallu beaucoup de temps pour découvrir iOS Pit. Cela s'est avéré réalisable dans la pratique. Des images de plusieurs mégaoctets peuvent être compressées dans les 200 Ko requis par notre backend ! Une telle méthode réalisable doit être montrée à tout le monde [ps : elles sont toutes créées en rassemblant les méthodes d’autres personnes, hehe~].

Actuellement, diverses nouvelles API HTML5 ont été bien implémentées sur le webkit mobile. Selon caniuse, les objets FileReader, Blob et Formdata utilisés dans cette démo ont été implémentés dans la plupart des navigateurs d'appareils mobiles (safari6.0+, android 3.0+), donc la compression des images directement sur le front-end est devenue une solution essentielle. fonctions de téléchargement d'images sur des appareils mobiles.

La compression et le téléchargement d'images sur le terminal mobile utilisent principalement les trois API h5 de filereader, canvas et formdata. La logique n'est pas difficile. L'ensemble du processus est :

(1) Lorsque l'utilisateur utilise le fichier d'entrée pour télécharger une image, utilisez filereader pour lire les données d'image téléchargées par l'utilisateur (format base64)

(2) Passer les données de l'image dans l'objet img, puis dessinez l'img sur le canevas, puis appelez canvas.toDataURL pour compresser l'image

(3) Obtenez les données d'image compressées au format base64, convertissez-les en binaire et insérez-les dans formdata, puis soumettez les formdata via XmlHttpRequest .

Dans ces trois étapes, la compression et le téléchargement de l'image sont terminés.

Cela semble simple, mais en réalité, il existe encore quelques pièges. Ensuite, utilisez le code pour analyser directement :

[1] Obtenez les données d'image

Obtenez d'abord les données d'image, qui doivent être surveillées l'événement de changement de fichier d'entrée, puis obtenez les fichiers objet de fichier téléchargés, convertissez les fichiers de type tableau en un tableau, puis effectuez une traversée forEach.

Déterminez ensuite le type de fichier. S'il ne s'agit pas d'une image, elle ne sera pas traitée. S'il s'agit d'une image, instanciez un lecteur de fichiers, lisez les données du fichier téléchargé au format base64 et déterminez la longueur des données. Si l'image est supérieure à 200 Ko, appelez la méthode compress pour la compresser, sinon appelez la méthode upload pour la télécharger.

filechooser.onchange = function() {
 if (!this.files.length) return;
 var files = Array.prototype.slice.call(this.files);
 if (files.length > 9) {
  alert("最多同时只可上传9张图片");
  return;
 }
 files.forEach(function(file, i) {
  if (!/\/(?:jpeg|png|gif)/i.test(file.type)) return;
  var reader = new FileReader();
  var li = document.createElement("li");
  li.innerHTML = &#39;<p class="progress"><span></span></p>&#39;;
  $(".img-list").append($(li));
  reader.onload = function() {
   var result = this.result;
   var img = new Image();
   img.src = result;
   //如果图片大小小于200kb,则直接上传
   if (result.length <= maxsize) {
    $(li).css("background-image", "url(" + result + ")");
    img = null;
    upload(result, file.type, $(li));
    return;
   }
   //图片加载完毕之后进行压缩,然后上传
   if (img.complete) {
    callback();
   } else {
    img.onload = callback;
   }
   function callback() {
    var data = compress(img);
    $(li).css("background-image", "url(" + data + ")");
    upload(data, file.type, $(li));
    img = null;
   }
  };
  reader.readAsDataURL(file);
 })
};

【2】Compresser les images

Après avoir obtenu les données d'image ci-dessus, vous pouvez utiliser la méthode de compression des images . La compression d'images n'implique pas de dessiner directement l'image sur le canevas, puis d'appeler toDataURL.

Dans IOS, il existe deux limitations pour dessiner des images sur toile :

La première est la taille de l'image. Si la taille de l'image dépasse deux millions de pixels, l'image ne peut pas être dessinée. sur le canevas, aucune erreur ne sera signalée lors de l'appel de drawImage, mais lorsque vous utilisez toDataURL pour obtenir des données d'image, vous obtiendrez des données d'image vides.

De plus, la taille de la toile est limitée. Si la taille de la toile est supérieure à environ cinq millions de pixels (c'est-à-dire le produit de la largeur et de la hauteur), non seulement l'image ne peut pas être dessinée, mais également d'autres choses ne peuvent pas être dessinées.

Pour faire face à la première limitation, la solution est de dessiner des tuiles. Le dessin de tuiles consiste à diviser l'image en plusieurs morceaux et à les dessiner sur la toile. La méthode dans mon code consiste à diviser l'image en 1 million de pixels, puis à la dessiner sur la toile.

Pour faire face à la deuxième limitation, ma solution consiste à compresser de manière appropriée la largeur et la hauteur de l'image. Pour des raisons de sécurité, la limite supérieure définie dans mon code est de quatre millions de pixels si l'image est plus grande. plus de quatre millions de pixels, compressez-le simplement à moins de 4 millions de pixels. Une image de quatre mégapixels devrait suffire, avec une largeur et une hauteur de 2000X2000.

Cela résout les deux limitations sur IOS.

En plus des limitations mentionnées ci-dessus, il existe deux pièges. Le premier est que le toDataURL de Canvas ne peut compresser que le format jpg. Lorsque l'image téléchargée par l'utilisateur est au format png, elle doit être convertie en jpg. Autrement dit, il peut être utilisé uniformément. canvas.toDataURL('image/jpeg', 0.1), le type est uniformément défini sur jpeg et le taux de compression est contrôlé par lui-même.

L'autre est que si vous convertissez un png en jpg, lorsque vous le dessinez sur une toile, si la toile a une zone transparente, la zone transparente deviendra noire lorsque vous la convertirez en jpg, car les pixels transparents de la toile est par défaut rgba ( 0,0,0,0), donc une fois converti en jpg, il devient rgba(0,0,0,1), c'est-à-dire que l'arrière-plan transparent deviendra noir. La solution est de poser un fond blanc sur la toile avant de dessiner.

function compress(img) {
 var initSize = img.src.length;
 var width = img.width;
 var height = img.height;
 //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
 var ratio;
 if ((ratio = width * height / 4000000) > 1) {
  ratio = Math.sqrt(ratio);
  width /= ratio;
  height /= ratio;
 } else {
  ratio = 1;
 }
 canvas.width = width;
 canvas.height = height;
 //铺底色
 ctx.fillStyle = "#fff";
 ctx.fillRect(0, 0, canvas.width, canvas.height);
 //如果图片像素大于100万则使用瓦片绘制
 var count;
 if ((count = width * height / 1000000) > 1) {
  count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
  //计算每块瓦片的宽和高
  var nw = ~~(width / count);
  var nh = ~~(height / count);
  tCanvas.width = nw;
  tCanvas.height = nh;
  for (var i = 0; i < count; i++) {
   for (var j = 0; j < count; j++) {
    tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
    ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
   }
  }
 } else {
  ctx.drawImage(img, 0, 0, width, height);
 }
 //进行最小压缩
 var ndata = canvas.toDataURL(&#39;image/jpeg&#39;, 0.1);
 console.log(&#39;压缩前:&#39; + initSize);
 console.log(&#39;压缩后:&#39; + ndata.length);
 console.log(&#39;压缩率:&#39; + ~~(100 * (initSize - ndata.length) / initSize) + "%");
 tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
 return ndata;
}

【3】Téléchargement de photos

完成图片压缩后,就可以塞进formdata里进行上传了,先将base64数据转成字符串,再实例化一个ArrayBuffer,然后将字符串以8位整型的格式传入ArrayBuffer,再通过BlobBuilder或者Blob对象,将8位整型的ArrayBuffer转成二进制对象blob,然后把blob对象append到formdata里,再通过ajax发送给后台即可。

XmlHttpRequest2中不仅可以发送大数据,还多出了比如获取发送进度的API,我代码里也进行了简单的实现。

//图片上传,将base64的图片转成二进制对象,塞进formdata上传
function upload(basestr, type, $li) {
 var text = window.atob(basestr.split(",")[1]);
 var buffer = new ArrayBuffer(text.length);
 var ubuffer = new Uint8Array(buffer);
 var pecent = 0,
  loop = null;
 for (var i = 0; i < text.length; i++) {
  ubuffer[i] = text.charCodeAt(i);
 }
 var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
 var blob;
 if (Builder) {
  var builder = new Builder();
  builder.append(buffer);
  blob = builder.getBlob(type);
 } else {
  blob = new window.Blob([buffer], {
   type: type
  });
 }
 var xhr = new XMLHttpRequest();
 var formdata = new FormData();
 formdata.append(&#39;imagefile&#39;, blob);
 xhr.open(&#39;post&#39;, &#39;/cupload&#39;);
 xhr.onreadystatechange = function() {
  if (xhr.readyState == 4 && xhr.status == 200) {
   console.log(&#39;上传成功:&#39; + xhr.responseText);
   clearInterval(loop);
   //当收到该消息时上传完毕
   $li.find(".progress span").animate({
    &#39;width&#39;: "100%"
   }, pecent < 95 ? 200 : 0, function() {
    $(this).html("上传成功");
   });
   $(".pic-list").append(&#39;<a href="&#39; + xhr.responseText + &#39;">&#39; + xhr.responseText + &#39;<img src="&#39; + xhr.responseText + &#39;" /></a>&#39;)
  }
 };
 //数据发送进度,前50%展示该进度
 xhr.upload.addEventListener(&#39;progress&#39;, function(e) {
  if (loop) return;
  pecent = ~~(100 * e.loaded / e.total) / 2;
  $li.find(".progress span").css(&#39;width&#39;, pecent + "%");
  if (pecent == 50) {
   mockProgress();
  }
 }, false);
 //数据后50%用模拟进度
 function mockProgress() {
  if (loop) return;
  loop = setInterval(function() {
   pecent++;
   $li.find(".progress span").css(&#39;width&#39;, pecent + "%");
   if (pecent == 99) {
    clearInterval(loop);
   }
  }, 100)
 }
 xhr.send(formdata);
}

至此,整个上传的前端图片压缩就完成了,因为是用了formdata提交,所以后台接数据的时候就跟普通form表单提交数据一样处理即可。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在Webpack中如何加载SVG

在vue-cli中如何实现移动端自适应

在Vue 组件Toast中如何实现显示框效果

有关webpack中rules参数处理

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn