Base64上传图片问题
Base64 Upload Image Issue
抱歉,这个问题很长,但我想尽可能地表达出来,并以一种易于理解的方式提出。我有一个程序允许用户使用 JavaScript 中的 croppie.js 裁剪图像,并将图像发送到后端 运行 Lisp 程序上的 Hunchentoot 服务器。用户上传后,我在将 Base64 图像保存到 .png 文件时遇到问题。将 post 请求发送到服务器后,我将 Base64 图像作为字符串获取,通过创建不包含 post 请求发送的标题的字符串子序列,从 Base64 请求中删除无效字符,并且还用“%”字符替换“+”字符,使 Base64 有效。接下来,我删除了字符串末尾的子字符串 +3D+3D,因为我在 Common Lisp 中使用的 s-base64 库抱怨 +3D+3D 是无效填充,我将其替换为“==”,这被认为有效填充。接下来,我通过使用 s-base64 库将 Base64 字符串转换为字节数组来创建一个字节数组,并将其存储在一个变量中。然后我遍历字节数组并将每个字节写入输出文件。完成后,我决定将字节数组的末尾打印到控制台,以便我可以查看输出和结束填充是否有效,看起来是这样。这是代码的一部分,注释使它更清晰:
(define-easy-handler (handle-image :uri "/handle-image.html") ()
(let ((data-source (hunchentoot:raw-post-data :force-text t))) ;get Base64 string
(let ((new-string (subseq data-source 36))) ;create a subsequence of Base64 string
(let ((final-string (substitute #\+ #\% new-string))) ;substitute % for +
(let ((end (search "+3D+3D" final-string))) ;find the invalid padding
(setf final-string (concatenate 'string (subseq final-string 0 end) "==")) ;add valid padding
(let ((byte-array (with-input-from-string (in final-string) ;create byte array (or simple-vector) out of Base64 string
(decode-base64-bytes in))))
(with-open-file (out "path/path/path/path/profile-image.png" ;create output stream to store file
:direction :output
:if-exists :supersede
:element-type 'unsigned-byte)
(dotimes (i (length byte-array)) ;write each byte from the byte array to output stream
(write-byte (aref byte-array i) out)))) ;close stream
(format t "!!!!!!!!: ~a" (subseq final-string (- (length final-string) 30))))))) ;print ending to console to ensure proper padding
"Upload Successful") ;send response to client
这是我的一些 JavaScript 代码:
$(document).ready(function(){
$image_crop = $('#image_demo').croppie({
enableExif: true,
viewport: {
width:200,
height:200,
type:'square' //circle
},
boundary:{
width:300,
height:300
}
});
如您所见,我首先创建裁剪器。我允许用户裁剪一个 200 x 200 的正方形,裁剪的总尺寸 space 是 300 x 300。这部分代码没有问题:
$('#upload_image').on('change', function(){
var reader = new FileReader();
reader.onload = function (event) {
$image_crop.croppie('bind', {
url: event.target.result
}).then(function(){
console.log('jQuery bind complete');
});
}
当他们 select 一个文件时,我将他们上传的图像绑定到裁剪器(就像你上传 Facebook 图像一样)。同样,没有问题:
reader.readAsDataURL(this.files[0]);
$('#uploadImageModal').modal('show');
上面我阅读了他们 select 编辑的文件,然后是模态 "pops up",就像您正在裁剪 Facebook 或 Instagram 照片一样:
$('.crop_image').click(function(event){
$image_crop.croppie('result', {
type: 'canvas',
size: 'viewport'
}).then(function(response){
$.ajax({
url:"handle-image.html",
type: "POST",
data:{"image": response},
success:function(data){
$('#uploadImageModal').modal('hide');
$('#uploaded_image').html(data);
}
});
})
});
上面我上传了ajax请求,如果上传成功他们会从服务器收到一条消息说上传成功,我也隐藏了图片裁剪的模态。
现在的问题是图像只是空白。我知道 Base64 是有效的,因为我使用了 Base64 转换工具来查看字符串是否有效。我还回去研究了位、字节、像素和图像尺寸,以了解计算机如何与它们交互,所以我不确定为什么我的图像只是显示空白。下面是裁剪器在我的网站上的样子:
绑定正常,然后我收到上传成功的消息。但是在写入图像并在文件系统中查看它之后,它要么是空白图像,要么有时会说图像类型不受支持。
我之所以在这个 post 中标记 PHP 是因为我确信有些人在 PHP 通过 [=40= 上传裁剪后的图像时遇到了类似的问题],其中一些解决方案可能适用于这种情况,但显然需要我将解决方案转换为 lisp 语法。我的假设是,当我将字符串转换为字节数组并将其写入文件时,我的代码出现了问题,但我认为如果我忽略了某些内容,post 代码的其他部分会很好.
正如 Brad 评论的那样,您应该首先尝试直接使用二进制上传。
除此之外:如果您在 base64 编码的字符串中遇到 %
,这很可能意味着整个字符串都另外进行了 URL 编码。一个快速的适当搜索给了 do-urlencode
作为一个库来解码它。将 %
替换为 +
可生成有效的 base64,但结果不一定代表有效的 jpg。
另外:使用 let*
而不是嵌套的 let
形式。也许使用 write-sequence
而不是按字节输出。
感谢@Brad 和@Svante 的回答,我得以解决问题。我决定将要上传的图像放在表单元素中,将 canvas 中的 blob 图像添加为表单的 FormData,然后通过 ajax post 发送 FormData请求:
$('.crop_image').on('click mousedown touchstart', function(event){ //When the crop image button is pressed event.
$image_crop.croppie('result', { //Get the result of the cropper
size: 'viewport', //Set the size to the viewport size (180 x 120)
format: 'png', //.png format
type: 'blob'
}).then(function (blob){
$form = $('#uploadForm');
var fd = new FormData($form);
fd.append('upload_image', blob);
$.ajax({
url: "handle-image.html",
type: "POST",
data: fd,
processData: false,
contentType: false,
success: function (data){
$('#uploadImageModal').modal('hide');
$('#uploaded_image').html(data);
}
});
})
});
这里我只是决定将croppie结果从类型"canvas"更改为类型"form",并指定它将是一个.png文件。从那里我将新创建的表单中的表单数据添加到 .ajax 请求并从那里开始。感谢@Brad 和@Svante 的帮助。
抱歉,这个问题很长,但我想尽可能地表达出来,并以一种易于理解的方式提出。我有一个程序允许用户使用 JavaScript 中的 croppie.js 裁剪图像,并将图像发送到后端 运行 Lisp 程序上的 Hunchentoot 服务器。用户上传后,我在将 Base64 图像保存到 .png 文件时遇到问题。将 post 请求发送到服务器后,我将 Base64 图像作为字符串获取,通过创建不包含 post 请求发送的标题的字符串子序列,从 Base64 请求中删除无效字符,并且还用“%”字符替换“+”字符,使 Base64 有效。接下来,我删除了字符串末尾的子字符串 +3D+3D,因为我在 Common Lisp 中使用的 s-base64 库抱怨 +3D+3D 是无效填充,我将其替换为“==”,这被认为有效填充。接下来,我通过使用 s-base64 库将 Base64 字符串转换为字节数组来创建一个字节数组,并将其存储在一个变量中。然后我遍历字节数组并将每个字节写入输出文件。完成后,我决定将字节数组的末尾打印到控制台,以便我可以查看输出和结束填充是否有效,看起来是这样。这是代码的一部分,注释使它更清晰:
(define-easy-handler (handle-image :uri "/handle-image.html") ()
(let ((data-source (hunchentoot:raw-post-data :force-text t))) ;get Base64 string
(let ((new-string (subseq data-source 36))) ;create a subsequence of Base64 string
(let ((final-string (substitute #\+ #\% new-string))) ;substitute % for +
(let ((end (search "+3D+3D" final-string))) ;find the invalid padding
(setf final-string (concatenate 'string (subseq final-string 0 end) "==")) ;add valid padding
(let ((byte-array (with-input-from-string (in final-string) ;create byte array (or simple-vector) out of Base64 string
(decode-base64-bytes in))))
(with-open-file (out "path/path/path/path/profile-image.png" ;create output stream to store file
:direction :output
:if-exists :supersede
:element-type 'unsigned-byte)
(dotimes (i (length byte-array)) ;write each byte from the byte array to output stream
(write-byte (aref byte-array i) out)))) ;close stream
(format t "!!!!!!!!: ~a" (subseq final-string (- (length final-string) 30))))))) ;print ending to console to ensure proper padding
"Upload Successful") ;send response to client
这是我的一些 JavaScript 代码:
$(document).ready(function(){
$image_crop = $('#image_demo').croppie({
enableExif: true,
viewport: {
width:200,
height:200,
type:'square' //circle
},
boundary:{
width:300,
height:300
}
});
如您所见,我首先创建裁剪器。我允许用户裁剪一个 200 x 200 的正方形,裁剪的总尺寸 space 是 300 x 300。这部分代码没有问题:
$('#upload_image').on('change', function(){
var reader = new FileReader();
reader.onload = function (event) {
$image_crop.croppie('bind', {
url: event.target.result
}).then(function(){
console.log('jQuery bind complete');
});
}
当他们 select 一个文件时,我将他们上传的图像绑定到裁剪器(就像你上传 Facebook 图像一样)。同样,没有问题:
reader.readAsDataURL(this.files[0]);
$('#uploadImageModal').modal('show');
上面我阅读了他们 select 编辑的文件,然后是模态 "pops up",就像您正在裁剪 Facebook 或 Instagram 照片一样:
$('.crop_image').click(function(event){
$image_crop.croppie('result', {
type: 'canvas',
size: 'viewport'
}).then(function(response){
$.ajax({
url:"handle-image.html",
type: "POST",
data:{"image": response},
success:function(data){
$('#uploadImageModal').modal('hide');
$('#uploaded_image').html(data);
}
});
})
});
上面我上传了ajax请求,如果上传成功他们会从服务器收到一条消息说上传成功,我也隐藏了图片裁剪的模态。
现在的问题是图像只是空白。我知道 Base64 是有效的,因为我使用了 Base64 转换工具来查看字符串是否有效。我还回去研究了位、字节、像素和图像尺寸,以了解计算机如何与它们交互,所以我不确定为什么我的图像只是显示空白。下面是裁剪器在我的网站上的样子:
绑定正常,然后我收到上传成功的消息。但是在写入图像并在文件系统中查看它之后,它要么是空白图像,要么有时会说图像类型不受支持。
我之所以在这个 post 中标记 PHP 是因为我确信有些人在 PHP 通过 [=40= 上传裁剪后的图像时遇到了类似的问题],其中一些解决方案可能适用于这种情况,但显然需要我将解决方案转换为 lisp 语法。我的假设是,当我将字符串转换为字节数组并将其写入文件时,我的代码出现了问题,但我认为如果我忽略了某些内容,post 代码的其他部分会很好.
正如 Brad 评论的那样,您应该首先尝试直接使用二进制上传。
除此之外:如果您在 base64 编码的字符串中遇到 %
,这很可能意味着整个字符串都另外进行了 URL 编码。一个快速的适当搜索给了 do-urlencode
作为一个库来解码它。将 %
替换为 +
可生成有效的 base64,但结果不一定代表有效的 jpg。
另外:使用 let*
而不是嵌套的 let
形式。也许使用 write-sequence
而不是按字节输出。
感谢@Brad 和@Svante 的回答,我得以解决问题。我决定将要上传的图像放在表单元素中,将 canvas 中的 blob 图像添加为表单的 FormData,然后通过 ajax post 发送 FormData请求:
$('.crop_image').on('click mousedown touchstart', function(event){ //When the crop image button is pressed event.
$image_crop.croppie('result', { //Get the result of the cropper
size: 'viewport', //Set the size to the viewport size (180 x 120)
format: 'png', //.png format
type: 'blob'
}).then(function (blob){
$form = $('#uploadForm');
var fd = new FormData($form);
fd.append('upload_image', blob);
$.ajax({
url: "handle-image.html",
type: "POST",
data: fd,
processData: false,
contentType: false,
success: function (data){
$('#uploadImageModal').modal('hide');
$('#uploaded_image').html(data);
}
});
})
});
这里我只是决定将croppie结果从类型"canvas"更改为类型"form",并指定它将是一个.png文件。从那里我将新创建的表单中的表单数据添加到 .ajax 请求并从那里开始。感谢@Brad 和@Svante 的帮助。