很抱歉,这个问题很长,但我想尽我所能表达,并以易于理解的方式提出。我有一个程序,允许用户使用JavaScript中的croppie.js裁剪图像,并将图像发送到运行Lisp程序的后端的Hunchentoot服务器。用户上载后,将Base64图像保存到.png文件时遇到问题。将发布请求发送到服务器后,我将以字符串的形式获取Base64映像,方法是通过创建字符串的子序列(不包含发布请求发送的标题),从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的正方形,裁切空间的总大小为300 x300。这部分代码没有问题:
$('#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');
});
}
上面,当他们选择文件时,我将他们上传的图像绑定到裁剪机(例如,如果您上传Facebook图像)。同样,没有问题:
reader.readAsDataURL(this.files[0]);
$('#uploadImageModal').modal('show');
上面我阅读了他们选择的文件,然后模态“弹出”,就好像您正在裁剪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转换工具来查看字符串是否有效。我还回过头来就位,字节,像素和图像尺寸进行了研究,以了解计算机如何与它们交互,因此我不确定为什么我的图像只是显示空白。这是我网站上的农作物的外观:
绑定正在工作,然后我收到消息,上传成功。但是在写入图像并在文件系统中查看它之后,它要么是空白图像,要么有时会说不支持该图像类型。
我在本文中标记PHP的原因是因为我确信有人在PHP中通过ajax上传裁剪后的图像时也遇到了类似的问题,其中某些解决方案可能适用于这种情况,但显然需要我将解决方案翻译为lisp语法。我的假设是,当我将字符串转换为字节数组并将其写入文件时,我的代码出了点问题,但是我认为如果我忽略了某些内容,最好将代码的其他部分发布。
答案 0 :(得分:2)
正如Brad所说,您应该首先尝试直接使用二进制上传。
此外:如果您在以base64编码的字符串中遇到%
,则很可能意味着整个内容都经过了URL编码。快速的proposos搜索使do-urlencode
作为解码它的库。用%
替换+
可以生成有效的base64,但是结果不一定表示有效的jpg。
也:使用let*
而不是嵌套的let
形式。也许使用write-sequence
代替字节输出。
答案 1 :(得分:0)
感谢@Brad和@Svante的回答,我得以解决问题。我决定将要上传的图像放在表单元素中,将画布中的Blob图像添加为表单的FormData,然后通过ajax发布请求发送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);
}
});
})
});
在这里,我刚刚决定将裁剪结果从“画布”类型更改为“表单”类型,并指定它将是一个.png文件。从那里,我将新创建的表单中的表单数据添加到.ajax请求中,然后从那里开始。感谢@Brad和@Svante在这里的帮助。