简体   繁体   English

Base64上传图片问题

[英]Base64 Upload Image Issue

Sorry for the long question, but I wanted to express it as well as I could, and ask it in a way that is understood easily.抱歉问了这么长的问题,但我想尽可能地表达它,并以易于理解的方式提问。 I have a program that allows a user to crop an image using croppie.js in JavaScript, and send the image to a Hunchentoot server on the backend running a Lisp program.我有一个程序,允许用户在 JavaScript 中使用croppie.js 裁剪图像,并将图像发送到后端运行 Lisp 程序的 Hunchentoot 服务器。 I am having an issue saving the Base64 image to a .png file once the user uploads it.用户上传后,将 Base64 图像保存到 .png 文件时出现问题。 Once the post request is sent to the server I am getting the Base64 image as a string, removing invalid characters from the Base64 request by creating a subsequence of the string without the heading sent by the post request and also substituting the "%" character for the "+" character to make the Base64 valid.将发布请求发送到服务器后,我将 Base64 图像作为字符串获取,通过创建字符串的子序列来从 Base64 请求中删除无效字符,而没有发布请求发送的标题,并替换为“%”字符使 Base64 有效的“+”字符。 Next I remove the substring +3D+3D at the end of my string, because the s-base64 library that I am using in Common Lisp complains +3D+3D is invalid padding, and I replace it with "==" which is considered valid padding.接下来,我删除字符串末尾的子字符串 +3D+3D,因为我在 Common Lisp 中使用的 s-base64 库抱怨 +3D​​+3D 是无效的填充,我将其替换为“==”,这被认为是有效的填充。 Next I create a byte array by using the s-base64 library to translate the Base64 string to a byte array, and store it in a variable.接下来我通过使用 s-base64 库将 Base64 字符串转换为字节数组来创建一个字节数组,并将其存储在一个变量中。 Then I loop through the byte array and write each byte to the output file.然后我遍历字节数组并将每个字节写入输出文件。 When that is finished I decided to print the end of the byte array to the console so that I could see if the output and ending padding is valid, which it appears to be.完成后,我决定将字节数组的结尾打印到控制台,以便我可以查看输出和结尾填充是否有效,这似乎是有效的。 Here is that part of the code, with comments to make it clearer:这是代码的那部分,并带有注释以使其更清晰:

(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

And here is some of my JavaScript code:这是我的一些 JavaScript 代码:

$(document).ready(function(){

     $image_crop = $('#image_demo').croppie({
        enableExif: true,
        viewport: {
          width:200,
          height:200,
          type:'square' //circle
        },
        boundary:{
          width:300,
          height:300
        }
   });

As you can see, I first create the cropper.如您所见,我首先创建了裁剪器。 I allow the user to have a 200 x 200 square to crop, and the total size of the cropping space is 300 x 300. There are no issues with that part of the code:我允许用户有一个 200 x 200 的正方形来裁剪,裁剪空间的总大小是 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');
  });
}

Above I bind the image they've uploaded to the cropper (like if you upload a Facebook image), when they select a file.在上面,当他们选择一个文件时,我将他们上传的图像绑定到裁剪器(就像您上传 Facebook 图像一样)。 Again, no issues:再次,没有问题:

 reader.readAsDataURL(this.files[0]);
$('#uploadImageModal').modal('show');

Above I read the file that they've selected and then the modal "pops up" as if you're cropping a Facebook or Instagram photo:上面我阅读了他们选择的文件,然后模态“弹出”,就像你在裁剪 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);
                     }
              });
          })
       });

Above I upload the ajax request, and if the upload was successful they will get a message from the server that it was success, and I hide the modal for image cropping as well.上面我上传了ajax请求,如果上传成功,他们会从服务器收到一条消息,它是成功的,我也隐藏了图像裁剪的模式。

Now the issue is that the image is simply blank.现在的问题是图像只是空白。 I know that the Base64 is valid because I used a Base64 conversion tool to see if the string was valid.我知道 Base64 是有效的,因为我使用了 Base64 转换工具来查看字符串是否有效。 I also went back and did my research in regards to Bits, Bytes, Pixels, and dimensions of images to see how the computer interacts with them, so i'm not sure why my image is simply displaying blank.我还回去研究了图像的位、字节、像素和尺寸,以了解计算机如何与它们交互,所以我不确定为什么我的图像只是显示为空白。 Here is a look at what the cropper looks like on my website:这是裁剪器在我的网站上的样子:

在此处输入图片说明

The bind is working, and then I get the message that the upload was successful.绑定正在运行,然后我收到上传成功的消息。 BUT after writing the image and viewing it in the file system it is either a blank image, or will sometimes say that the image type is not supported.但是在写入图像并在文件系统中查看后,它要么是空白图像,要么有时会说不支持该图像类型。

The reason why I am tagging PHP in this post is because I am sure some people have had similar issues in PHP with uploading a cropped image via ajax, and some of those solutions might be applicable in this case, but obviously will require me translating the solution to the lisp syntax.我在这篇文章中标记 PHP 的原因是因为我确信有些人在通过 ajax 上传裁剪图像时在 PHP 中遇到了类似的问题,其中一些解决方案可能适用于这种情况,但显然需要我翻译lisp 语法的解决方案。 My assumption is that something is wrong with my code when I translate the string to a byte array and write it to a file, but I thought it'd be good to post other sections of my code if I am overlooking something.我的假设是,当我将字符串转换为字节数组并将其写入文件时,我的代码有问题,但我认为如果我忽略了某些内容,最好发布代码的其他部分。

As Brad commented, you should first try to use binary uploads directly.正如 Brad 评论的那样,您应该首先尝试直接使用二进制上传。

That aside: if you encounter a % in a base64-encoded string, it most likely means that the entire thing is additionally URL-encoded.除此之外:如果您在 base64 编码的字符串中遇到% ,则很可能意味着整个内容是另外的 URL 编码的。 A quick apropos search gave do-urlencode as a library to decode that.快速的适当搜索将do-urlencode作为一个库来解码。 Replacing % with + makes valid base64, but the result does not necessarily represent valid jpg.+替换%使 base64 有效,但结果不一定代表有效的 jpg。

Also: use let* instead of nested let forms.另外:使用let*而不是嵌套的let表单。 Maybe use write-sequence instead of byte-wise output.也许使用write-sequence而不是按字节输出。

Thanks to the answer from @Brad and @Svante I was able to solve the problem.感谢@Brad 和@Svante 的回答,我能够解决这个问题。 I decided to put the image that I want to upload within a form element, add the blob image from the canvas as FormData for the form, and then send the FormData via an ajax post request:我决定将要上传的图像放在表单元素中,将画布中的 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);
        }
        });
    })
 });

Here I just decided to change the croppie result from the type "canvas"to the type "form", and specify that it will be a .png file.在这里,我决定将croppie 结果从“canvas”类型更改为“form”类型,并指定它将是一个.png 文件。 From there I added the form data from my newly created form to the .ajax request and went from there.从那里我将新创建的表单中的表单数据添加到 .ajax 请求并从那里开始。 Thanks to @Brad and @Svante for the help here.感谢@Brad 和@Svante 在此提供帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM