来自 前端知识 2019-11-14 17:53 的文章
当前位置: 金沙澳门官网网址 > 前端知识 > 正文

前端实现,API缩放并上传图片完整示例

标题 1 :浏览器对 canvas 节制

Canvas 的 W3C 的专门的职业上并没有提起 canvas 的最大高/宽度和面积,然而种种厂商的浏览器出于浏览器质量的考虑,在分歧的阳台上安装了最大的高/宽度也许是渲染面积,当先了那个阈值渲染的结果会是空白。测量试验了三种浏览器的 canvas 品质如下:

  • chrome (版本 46.0.2490.80 (64-bit))
    • 最大范围:268, 435, 456 px^2 = 16, 384 px * 16, 384 px
    • 最大宽/高:32, 767 px
  • firefox (版本 42.0)
    • 最大规模:32, 767 px * 16, 384 px
    • 最大宽/高:32, 767px
  • safari (版本 9.0.1 (11601.2.7.2))
    • 最大范围: 268, 435, 456 px^2 = 16, 384 px * 16, 384 px
  • ie 10(版本 10.0.9200.17414)
    • 最大宽/高: 8, 192px * 8, 192px

在形似的 web 应用中,只怕超级少会超越这么些节制。不过,假诺越过了那些节制,则 会招致导出为空白或然由于内部存款和储蓄器败露招致浏览器崩溃。

并且从叁只来说, 导出 png 也是生龙活虎项很费用内部存款和储蓄器的操作,粗略估量一下,导出 16, 384 px * 16, 384 px 的 svg 会消耗 16384 * 16384 * 4 / 1024 / 1024 = 1024 M 的内部存款和储蓄器。所以,在相像那些极限值的时候,浏览器也会 反应变慢,能不能导出成功也跟系统的可用内部存款和储蓄器大小等等都有关系。

对于这几个主题素材,犹如下二种减轻方法:

  1. 将数据发送给后端,在后端完成 调换;
  2. 前者将 svg 切分成七个图片导出;

首先种办法能够应用 PhantomJS、inkscape、ImageMagick 等工具,相对来讲比较容易,这里大家珍视探索第二种缓和方式。

代码如下:

难点 2 :导出包括图表的 svg

在导出的时候,还有或然会遇上另三个难题:如果 svg 里面饱含图表,你会发觉经过上述办法导出的 png 里面,原来的图形是不出示的。平日感到是 svg 里面富含的图片跨域了,可是只要您把这么些图片换花费域的图形,依旧会师世这种情景。澳门金莎娱乐手机版 1

图形中上部分是导出前的 svg,下图是导出后的 png。svg 中的图片是本域的,在导出后不展现。

在上头的例子中,你能够使用canvas 的 toDataU哈弗L() 方法获得图像的 Base64编码的值(能够附近明白为16进制字符串,可能二进制数据流).
潜心: canvas 的 toDataURL() 获取的ULacrosseL以字符串起初,有23个不算的数据 "data:image/png;base64,",须要在顾客端照旧服务端进行过滤.
条件上如若浏览器帮衬,U奥德赛L地址的长度是向来不范围的,而1024的尺寸约束,是老一代IE所独有的。

前言

svg 是生机勃勃种矢量图形,在 web 上选用很普及,但是过多时候由于接收的现象,日常须要将 svg 转为 png 格式,下载到本地等。随着浏览器对 HTML 5 的支撑度更高,大家能够把 svg 转为 png 的劳作付出浏览器来变成。

代码如下:

主题素材来自

大家遵照文章最早阶提议的步调,稳步各种调查,会发觉在率先步的时候,svg 中的图片就不出示了。也正是,当 image 元素的 src 为叁个 svg,而且 svg 里面含有图表,那么被含有的图样是不会来得的,即便这么些图形是本域的。

W3C 关于那一个主题素材并不曾 做表明,最终在  找到了关于这些难点的印证。 意思是:禁绝这么做是由于安全着想,svg 里面引用的保有 表面财富 包涵image, stylesheet, script 等都会被拦截。

中间还举了一个例子:假若未有那些界定,假使三个论坛允许用户上传那样的 svg 作为头像,就有十分大或许现身如此的场景,壹个人黑客上传 svg 作为头像,里面含有代码:<image xlink:href="http://evilhacker.com/myimage.png">(要是那位黑客具有对于 evil骇客.com 的调整权卡塔 尔(英语:State of Qatar),那么那位黑客就全盘能成就下边包车型客车事情:

  • 倘若有人查看他的资料,evil红客.com 就能收下到壹遍 ping 的伸手(进而能够得到查看者的 ip卡塔 尔(英语:State of Qatar);
  • 能够成功对于区别的 ip 地址的人展现不平等的头像;
  • 澳门金莎娱乐手机版 ,可以每一日转变头像的外观(而不用经过论坛管理员的调查卡塔 尔(阿拉伯语:قطر‎。

看到这里,大约就精晓了方方面面难点的始最后,当然还应该有少数缘故或然是制止图像递归。

代码如下:

消亡办法

思路:由于安全因素,其实首先步的时候,图片已经体现不出来了。那么大家明日构思的章程是在率先步之后遍历 svg 的结构,将有着的 image 成分的 url、地点和尺寸保存下去。在第三步之后,按梯次贴到 canvas 上。那样,末了导出的 png 图片就能够有 svg 里面包车型客车 image。关键代码

JavaScript

// 此处略去变通 svg url 的长河 var svgUrl = DomU宝马7系L.createObjectUEnclaveL(blob); var svgWidth = document.querySelector('#kity_svg').getAttribute('width'); var svgHeight = document.querySelector('#kity_svg').getAttribute('height'); var embededImages = document.querySelectorAll('#kity_svg image'); // 由 nodeList 转为 array embededImages = Array.prototype.slice.call(embededImages); // 加载底层的图 loadImage(svgUrl).then(function(img) { var canvas = document.createElement('canvas'), ctx = canvas.getContext("2d"); canvas.width = svgWidth; canvas.height = svgHeight; ctx.drawImage(img, 0, 0); // 遍历 svg 里面全体的 image 成分embededImages.reduce(function(sequence, svgImg){ return sequence.then(function() { var url = svgImg.getAttribute('xlink:href') + 'abc', dX = svgImg.getAttribute('x'), dY = svgImg.getAttribute('y'), dWidth = svgImg.getAttribute('width'), dHeight = svgImg.getAttribute('height'); return loadImage(url).then(function( sImg) { ctx.drawImage(sImg, 0, 0, sImg.width, sImg.height, dX, dY, dWidth, dHeight); }, function(err) { console.log(err); }); }, function(err) { console.log(err); }); }, Promise.resolve()).then(function() { // 希图在后面一个下载 var a = document.createElement("a"); a.download = 'download.png'; a.href = canvas.toDataU福睿斯L("image/png"); var clickEvent = new MouseEvent("click", { "view": window, "bubbles": true, "cancelable": false }); a.dispatchEvent(clickEvent); }); }, function(err) { console.log(err); }) // 省略了 loadImage 函数 // 代码和率先个例证同样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 此处略去生成 svg url 的过程
var svgUrl = DomURL.createObjectURL(blob);
var svgWidth = document.querySelector('#kity_svg').getAttribute('width');
var svgHeight = document.querySelector('#kity_svg').getAttribute('height');
 
var embededImages = document.querySelectorAll('#kity_svg image');
// 由 nodeList 转为 array
embededImages = Array.prototype.slice.call(embededImages);
// 加载底层的图
loadImage(svgUrl).then(function(img) {
 
var canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
 
canvas.width = svgWidth;
canvas.height = svgHeight;
 
ctx.drawImage(img, 0, 0);
    // 遍历 svg 里面所有的 image 元素
    embededImages.reduce(function(sequence, svgImg){
 
        return sequence.then(function() {
            var url = svgImg.getAttribute('xlink:href') + 'abc',
                dX = svgImg.getAttribute('x'),
                dY = svgImg.getAttribute('y'),
                dWidth = svgImg.getAttribute('width'),
                dHeight = svgImg.getAttribute('height');
 
            return loadImage(url).then(function( sImg) {
                ctx.drawImage(sImg, 0, 0, sImg.width, sImg.height, dX, dY, dWidth, dHeight);
            }, function(err) {
                console.log(err);
            });
        }, function(err) {
            console.log(err);
        });
    }, Promise.resolve()).then(function() {
        // 准备在前端下载
        var a = document.createElement("a");
        a.download = 'download.png';
        a.href = canvas.toDataURL("image/png");
 
        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
 
        a.dispatchEvent(clickEvent);
 
        });
 
      }, function(err) {
        console.log(err);
   })
 
   // 省略了 loadImage 函数
   // 代码和第一个例子相同

说明

  1. 事例中 svg 里面包车型客车图疑似根节点上面包车型地铁,由此用于表示地方的 x, y 直接取来就可以使用,在实际上中,那么些地方只怕必要跟任何属性做一些运算之后得出。假如是根据svg 库创设的,那么能够直接使用Curry面用于固定的函数,比一贯从底层运算特别有益和标准。
  2. 小编们这里研讨的是本域的图片的导出难题,跨域的图纸由于「污染了」画布,在实行 toDataUrl 函数的时候会报错。

// 加载 图像文件(url路线)
function loadImage(src){
// 过滤掉 非 image 类型的公文
if(!src.type.match(/image.*/)){
if(window.console){
console.log("选用的文件类型不是图片: ", src.type);
} else {
window.confirm("只可以选拔图片文件");
}
return;
}
// 创设 FileReader 对象 并调用 render 函数来成功渲染.
var reader = new FileReader();
// 绑定load事件自动回调函数
reader.onload = function(e){
// 调用前边的 render 函数
render(e.target.result);
};
// 读取文件内容
reader.readAsDataURL(src);
};

前面贰个实现 SVG 转 PNG

2015/11/16 · JavaScript · PNG, SVG

初藳出处: 百度FEX - zhangbobell   

汤姆 Trenka 能为"小编"的博客写意气风发篇随笔,对本人的话是八个宏大的得体。汤姆是Dojo框架的最早进献者之豆蔻年华,也是本身在SitePen集团的君子之交.我见证了她最一级的天才手艺,何况他连连第三个从前瞻性的缓和方案预知了重重吃力的主题素材。他连续几天站在局外考虑,别具一格但却又结实可信赖地缓慢解决边缘难题。本文便是叁个周密的事例。
近日自己三番四遍被问道要开创一个顾客接口API,允许顾客上传图片到服务器上(伴随别的的专门的学业),并能在大家商家提供匡助的大方网址的顾客端上利用。日常来说那都以超轻便的事务——创造二个form表单,增加叁个file类型的input输入框,让客户从Computer里甄选图片,并在form标签上设置enctype="multipart/form-data"表单属性,然后上传就能够。很简单,不是吗?事实上,这里有一个十足轻巧的例子;点击步向
不过纵然您想要通过一些方式初期管理一下图形再上传,那该怎么做?比如说,你必得先减削图片尺寸,可能需求图片只可以是有些品种的格式,如 png 大概jpg,你怎么做?
用canvas来解决!

svg 切分成多少个图片导出

思路:浏览器即便对 canvas 有尺寸和面积的范围,然则对于 image 成分并未鲜明的界定,也正是率先步生成的 image 其实显示是健康的,大家要做的只是在其次步 dragImage 的时候分数14回将 image 成分切分并贴到 canvas 上然后下载下来。 同有时间,应留神到 image 的载入是叁个异步的历程。

要害代码

JavaScript

// 构造 svg Url,此处省略将 svg 经字符过滤后转为 url 的长河。 var svgUrl = DomU奥迪Q3L.createObjectU景逸SUVL(blob); var svgWidth = document.querySelector('#kity_svg').getAttribute('width'); var svgHeight = document.querySelector('#kity_svg').getAttribute('height'); // 分片的宽窄和冲天,可依附浏览器做适配 var w0 = 8192; var h0 = 8192; // 每行和每列能宽容的分片数 var M = Math.ceil(svgWidth / w0); var N = Math.ceil(svgHeight / h0); var idx = 0; loadImage(svgUrl).then(function(img) { while(idx < M * N) { // 要分开的面片在 image 上的坐标和尺寸 var targetX = idx % M * w0, targetY = idx / M * h0, targetW = (idx + 1) % M ? w0 : (svgWidth - (M - 1) * w0), targetH = idx >= (N - 1) * M ? (svgHeight - (N - 1) * h0) : h0; var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = targetW; canvas.height = targetH; ctx.drawImage(img, targetX, targetY, targetW, targetH, 0, 0, targetW, targetH); console.log('now it is ' + idx); // 策动在前面一个下载 var a = document.createElement('a'); a.download = 'naotu-' + idx + '.png'; a.href = canvas.toDataUTucsonL('image/png'); var click伊芙nt = new Mouse伊夫nt('click', { 'view': window, 'bubbles': true, 'cancelable': false }); a.dispatch伊夫nt(click伊芙nt); idx++; } }, function(err) { console.log(err); }); // 加载 image function loadImage(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.src = url; image.crossOrigin = 'Anonymous'; image.onload = function() { resolve(this); }; image.onerror = function(err) { reject(err); }; }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 构造 svg Url,此处省略将 svg 经字符过滤后转为 url 的过程。
var svgUrl = DomURL.createObjectURL(blob);
var svgWidth = document.querySelector('#kity_svg').getAttribute('width');
var svgHeight = document.querySelector('#kity_svg').getAttribute('height');
 
// 分片的宽度和高度,可根据浏览器做适配
var w0 = 8192;
var h0 = 8192;
 
// 每行和每列能容纳的分片数
var M = Math.ceil(svgWidth / w0);
var N = Math.ceil(svgHeight / h0);
 
var idx = 0;
loadImage(svgUrl).then(function(img) {
 
    while(idx < M * N) {
        // 要分割的面片在 image 上的坐标和尺寸
        var targetX = idx % M * w0,
            targetY = idx / M * h0,
            targetW = (idx + 1) % M ? w0 : (svgWidth - (M - 1) * w0),
            targetH = idx >= (N - 1) * M ? (svgHeight - (N - 1) * h0) : h0;
 
        var canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d');
 
            canvas.width = targetW;
            canvas.height = targetH;
 
            ctx.drawImage(img, targetX, targetY, targetW, targetH, 0, 0, targetW, targetH);
 
            console.log('now it is ' + idx);
 
            // 准备在前端下载
            var a = document.createElement('a');
            a.download = 'naotu-' + idx + '.png';
            a.href = canvas.toDataURL('image/png');
 
            var clickEvent = new MouseEvent('click', {
                'view': window,
                'bubbles': true,
                'cancelable': false
            });
 
            a.dispatchEvent(clickEvent);
 
        idx++;
    }
 
}, function(err) {
    console.log(err);
});
 
// 加载 image
function loadImage(url) {
    return new Promise(function(resolve, reject) {
        var image = new Image();
 
        image.src = url;
        image.crossOrigin = 'Anonymous';
        image.onload = function() {
            resolve(this);
        };
 
        image.onerror = function(err) {
            reject(err);
        };
    });
}

说明:

  1. 鉴于在后边叁个下载有浏览器包容性、客户体验等难题,在事实上中,或者须求将调换后的数量发送到后端,并作为三个压缩包下载。
  2. 分片的尺码这里运用的是 8192 * 9192,在实际上中,为了巩固宽容性和经验,能够凭借浏览器和平台做适配,比如在 iOS 下的 safari 的最大范围是 4096 *4096。

function init(){
// 获取DOM成分对象
var target = document.getElementById("drop-target");
// 阻止 dragover(拖到DOM成分上方) 事件传递
target.addEventListener("dragover", function(e){e.preventDefault();}, true);
// 拖动并推广鼠标的风浪
target.addEventListener("drop", function(e){
// 阻止暗中同意事件,以至事件传播
e.preventDefault();
// 调用前面的加载图像 函数,参数为dataTransfer对象的首先个文本
loadImage(e.dataTransfer.files[0]);
}, true);
var setheight = document.getElementById("setheight");
var maxheight = document.getElementById("maxheight");
setheight.addEventListener("click", function(e){
//
var value = maxheight.value;
if(/^d+$/.test(value)){
MAX_HEIGHT = parseInt(value);
}
e.preventDefault();
},true);
var btnsend = document.getElementById("btnsend");
btnsend.addEventListener("click", function(e){
//
sendImage();
},true);
};

诚如方法

  1. 创建 imageimage,src = xxx.svg;
  2. 开创 canvas,dragImage 将图纸贴到 canvas 上;
  3. 运用 toDataUrl 函数,将 canvas 的意味为 url;
  4. new image, src = url, download = download.png;

可是,在转移的时候不常有的时候会遇见如下的如下的七个难点:

// 参数,最大惊人
var MAX_HEIGHT = 100;
// 渲染
function render(src){
// 创建三个 Image 对象
var image = new Image();
// 绑定 load 事件微机,加载成功后施行
image.onload = function(){
// 获取 canvas DOM 对象
var canvas = document.getElementById("myCanvas");
// 若是高度超过标准
if(image.height > MAX_HEIGHT) {
// 宽度等比例缩放 *=
image.width *= MAX_HEIGHT / image.height;
image.height = MAX_HEIGHT;
}
// 获取 canvas的 2d 意况目的,
// 能够清楚Context是组织者,canvas是房子
var ctx = canvas.getContext("2d");
// canvas清屏
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重置canvas宽高
canvas.width = image.width;
canvas.height = image.height;
// 将图像绘制到canvas上
ctx.drawImage(image, 0, 0, image.width, image.height);
// !!! 注意,image 未有投入到 dom之中
};
// 设置src属性,浏览器会自行加载。
// 记住必得先绑定事件,本领设置src属性,不然会出三头难点。
image.src = src;
};

结语

在那间和贵胄揠苗助长了 在前端将 svg 转为 png 的章程和进度中也许会遭受的多个难题,一个是浏览器对 canvas 的尺码节制,另二个是导出图片的主题材料。当然,那多少个难点还应该有其余的减轻办法,同不时间鉴于文化所限,本文内容难免有漏洞,应接大家商量指正。最终谢谢@techird 和 @Naxior 关于那多个难点的座谈。

1 赞 2 收藏 评论

澳门金莎娱乐手机版 2

代码如下:

jQuery 达成如下:

OK,化解!你还索要做的,正是创办叁个只管的客户分界面,并允许你决定图片的尺寸。上传到劳动器端的数额,并无需管理enctype为 multi-part/form-data 的气象,仅仅一个大致的POST表单管理程序就能够了.
好了,上面附上完整的代码示例:

File API简介
新的File API接口是在不违背任何安全沙盒法规下,读取和列出客商文件目录的一个门道—— 通过沙盒(sandbox)约束,恶意网站并无法将病毒写入客户磁盘,当然更无法举行。
咱俩要运用的文件读取对象叫做 FileReader,FileReader允许开辟者读取文件的从头到尾的经过(具体浏览器的兑现格局只怕大不雷同)。

点评:成立三个只管的顾客分界面,并同意你说了算图片的高低。上传到劳动器端的数目,并无需管理enctype为 multi-part/form-data 的场面,仅仅三个简便的POST表单管理程序就能够了. 好了,上面附上完整的代码示例

大器晚成旦大家早已获得了图像文件的path路线,那么正视前面包车型客车代码,使用FileReader来加载和渲染图像就变得相当的轻巧了:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="sun.misc.BASE64Decoder"%>
<%@page import="java.io.*"%>
<%@page import="org.springframework.web.util.UriComponents"%>
<%@page import="java.net.URLDecoder"%>
<%!
// 本文件:/receive.jsp
// 图片贮存路径
String photoPath = "D:/blog/upload/photo/";
File photoPathFile = new File(photoPath);
// references:
private boolean saveImageToDisk(byte[] data,String imageName) throws IOException{
int len = data.length;
//
// 写入到文件
FileOutputStream outputStream = new FileOutputStream(new File(photoPathFile,imageName));
outputStream.write(data);
outputStream.flush();
outputStream.close();
//
return true;
}
private byte[] decode(String imageData) throws IOException{
BASE64Decoder decoder = new BASE64Decoder();
byte[] data = decoder.decodeBuffer(imageData);
for(int i=0;i<data.length;++i)
{
if(data[i]<0)
{
//调解极其数据
data[i]+=256;
}
}
//
return data;
}
%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%
//要是是IE,那么必要设置为text/html,不然会弹框下载
//response.setContentType("text/html;charset=UTF-8");
response.setContentType("application/json;charset=UTF-8");
//
String imageName = request.getParameter("imagename");
String imageData = request.getParameter("imagedata");
int success = 0;
String message = "";
if(null == imageData || imageData.length() < 100){
// 数据太短,鲜明不创设
message = "上传失败,数据太短或海市蜃楼";
} else {
// 去除开头不客观的数码
imageData = imageData.substring(30);
imageData = URLDecoder.decode(imageData,"UTF-8");
//System.out.println(imageData);
byte[] data = decode(imageData);
int len = data.length;
int len2 = imageData.length();
if(null == imageName || imageName.length() < 1){
imageName = System.currentTimeMillis()+".png";
}
saveImageToDisk(data,imageName);
//
success = 1;
message = "上传成功,参数长度:"+len2+"字符,拆解解析文件大小:"+len+"字节";
}
// 后台打字与印刷
System.out.println("message="+message);
%>
{
"message": "<%=message %>",
"success": <%=success %>
}

本文由金沙澳门官网网址发布于前端知识,转载请注明出处:前端实现,API缩放并上传图片完整示例

关键词: