如何防止编辑表单时上传的图片被删除?

How to prevent uploaded image from getting deleted when editing a form?

我正在开发一款允许用户 post 活动的应用程序。我正在使用 NodeJS、Express 和 Mongo.

我创建了一个表单,允许用户输入活动详细信息,并上传与活动相关的图片。我还创建了一个允许用户编辑事件详细信息的表单。

表格如下所示:

问题:

  1. 用户用事件详细信息填写表单并附上图片。
  2. 用户提交表单
  3. 用户决定他想更改事件标题,但没有别的
  4. 用户点击编辑事件,更改标题,然后提交
  5. 问题:即使用户没有删除与事件关联的图片,该图片也不再存在。

这是我的 new.ejs 文件的一部分(对于 post 新事件,只是添加到这里以供参考)

<script type="text/javascript" src="/js/eventform.js"></script>
<form action="/events" 
      method="POST" 
      enctype="multipart/form-data" 
      onSubmit="return(validate(this));" // validating user input
      novalidate >

    ....
    ....
    <input name="image" type="file" id="image" accept="image/*" style="display:none" onchange="handleFiles(this.files)">
    <div id="imageBorder" >
        <div id="imageContainer">
            <div id="dropbox">
                 <i class="fa fa-picture-o" aria-hidden="true"></i>
                 <p> Drop image here or click to upload</p>
             </div>
             <div id="preview" class="hidden">
             </div>
             <button id="fileSelect" class="...">Upload Image</button>
             <button id="fileRemove" class="...">Remove Image</button>
        </div>
     </div>
     ....
     ....
</form>

请注意,我正在使用隐藏的输入字段。我还有两个 div,预览(最初隐藏)和保管箱。上传图片时,class 'hidden' 从预览中删除并添加到保管箱。

这里是部分js文件newevent.js

$(document).ready(function() {
    ....
    eventImageSetup();
    ....
}
....
function eventImageSetup() {
    var dropbox = document.getElementById("dropbox"),
        fileElem = document.getElementById("image"),
        fileSelect = document.getElementById("fileSelect"),
        fileRemove = document.getElementById("fileRemove");
    $(dropbox).height($('#imageBorder').height());
    fileSelect.addEventListener("click", function(e) {
        if (fileElem) {
          fileElem.click();
          e.preventDefault(); // to prevent submit
        }
    }, false);
    fileRemove.addEventListener("click", function(e) {
        e.preventDefault(); // prevent submit
        if(!$('#preview').hasClass('hidden')) { // if there is an image 
            $('#preview').empty(); 
            $('#dropbox').removeClass('hidden');
            $('#preview').addClass('hidden');
            $('#fileSelect').text('Upload Image');
            $('#image').wrap('<form>').closest('form').get(0).reset();
             $('#image').unwrap();
        }    
        removeError($('#imageError'), $('#image'));
    });
    dropbox.addEventListener("dragenter", dragenter, false);
    dropbox.addEventListener("dragover", dragover, false);
    dropbox.addEventListener("drop", drop, false);
}

function handleFiles(files) { 
   var file = files[0];
   .... // some error checking

   var img = document.createElement("img");
   img.id = "uploadedImage";
   img.file = file;
   img.onload = function() {
          adjustImageSize(img);
   };
   $('#dropbox').addClass('hidden');
   $('#preview').removeClass('hidden');
   $('#preview').empty();
   $('#preview').append(img);
   $('#fileSelect').text('Replace Image');

   var reader = new FileReader();
          reader.onload = (function(aImg) {
                 return function(e) {
                 aImg.src = e.target.result;
          };
    })(img);
    reader.readAsDataURL(file);
}    

这是我的 edit.ejs 文件的一部分

<form action="/events/<%=event._id%>?_method=PUT" 
      method="POST"
      enctype="multipart/form-data" 
      onSubmit="return(validate(this));"
      novalidate >

    <input name="image" type="file" id="image" accept="image/*" style="display:none" onchange="handleFiles(this.files)">

    <div id="imageBorder" >
        <div id="imageContainer">
              <div id="dropbox" class="hidden">
                    <i class="fa fa-picture-o" aria-hidden="true"></i>
                    <p> Drop image here or click to upload</p>
               </div>
               <div id="preview">
                    <script>
                        var imageExists = '<%=event.image%>';
                        if(imageExists) {
                            var myImg = document.createElement("IMG");
                            var source = "../../<%= event.image %>";
                            myImg.src = source; 
                            adjustImageSize(myImg);
                            $('#preview').append(myImg);
                        }
                        </script>
               </div>
               <button id="fileSelect" class="...">Upload Image</button>
               <button id="fileRemove" class="...">Remove Image</button>
            </div>
     </div> <!-- END OF imageBorder -->
     ....
 </form>

上面的脚本成功让图片出现在编辑页面,如下图。

但是当你点击提交时,图片并没有出现。

这里是nodejs路由文件。你可以在这里看到问题

// UPDATE SPECIFIC EVENT IN DATABASE 
router.put("/:id", upload.single('image'), middleware.checkEventOwnership, function(req, res) {

    var filepath = undefined;
    if(req.file) {
        filepath = req.file.path.substr(7); // Substr to remove "/public"
    }

    req.body.image = filepath;

    Event.findByIdAndUpdate(req.params.id, req.body, function(err, foundEvent) {
       if(err) {
           console.log(err);
           req.flash("error", err);
       } else {
           req.flash("success", "Successfully edited your event");
       }
       res.redirect("/events/" + req.params.id);
    });
});

基本上,如果我在编辑表单中保持图像不变,req.file 将不存在。因此,req.body.image = 未定义。并且图像不再与事件相关联。

常识会说这样做

    if(req.file) {
        filepath = req.file.path.substr(7); 
        req.body.image = filepath;
    }

但如果这样做,就会引入一个新问题:如果用户编辑事件并删除图像(即决定他不想要与事件关联的图像),则图像永远不会被删除。

知道如何解决这个问题吗? 我知道我必须在 edit.ejs 脚本中做一些事情...更具体地说,我需要创建一个图像文件...但我不确定如何处理这个

所以我通过一个我真的不喜欢的黑客来实现它。我确信有一种更好、更清晰、更标准的方式来处理 edit.ejs 和图像。换句话说,请帮我找到更好的解决方案!

以下是 edit.ejs

中的变化
<form action="/events/<%=event._id%>?_method=PUT" 
      method="POST"
      enctype="multipart/form-data" 
      onSubmit="return validate(this) & editPageImageProcessing(this);"
      novalidate >
     ....
     ....
       <div id="preview">
          <script>
                  var imageExists = '<%=event.image%>';
                  if(imageExists) {
                         var myImg = document.createElement("IMG");
                         var source = "../../<%= event.image %>";
                         myImg.src = source; 
                         myImg.name = "previewImage";
                         myImg.id = "previewImage";
                         adjustImageSize(myImg);
                         $('#preview').append(myImg);
                   }
          </script>
      </div>

基本上,我添加了行

                         myImg.name = "previewImage";
                         myImg.id = "previewImage";

并添加了一个函数 editPageImageProcessing。

这个函数的作用是:如果用户没有上传新图片,也没有删除图片,创建一个隐藏的输入字段"hiddenImage",并让其值为原始的来源图片。见下文:

// This function deals with edit image
function editPageImageProcessing(form) {
    // If the user didn't change the image
             // preview would be NOT hidden
             // there would not be a req.file (document.getElementById('image').val == '')
             // we want a hiddenimage input field
    var aFile = document.getElementById('image').value;
    console.log("File: ", aFile);
    var preview = document.getElementById('preview');
    console.log("Preview has hidden class: " + $(preview).hasClass('hidden'));
    if(aFile == '' && !$(preview).hasClass('hidden')) {
        var input = document.createElement('input');
        $(input).attr("name", "hiddenImage");
        $(input).attr("id", "hiddenImage");
        $(input).attr("type", "hidden");
        var myImage = document.getElementById('previewImage');
        $(input).attr("value", myImage.src);
        $('form').append(input); 
    }
    return true;
}

现在,在编辑路径中,我这样做了

// UPDATE SPECIFIC EVENT IN DATABASE 
router.put("/:id", upload.single('image'), middleware.checkEventOwnership, function(req, res) {
    var filepath = undefined;
    if(req.file) { // If user uploaded a new image
        filepath = req.file.path.substr(7); // Substr to remove "/public"
        console.log(filepath);
    } else if(req.body.hiddenImage) { // If user kept the same image
        var index = req.body.hiddenImage.lastIndexOf("/uploads");
        filepath = req.body.hiddenImage.substr(index);
        // req.body.hiddenImage WILL ONLY EXIST if user left image unchanged
    }

    req.body.image = filepath; // If user deleted image, this will be undefined, which is what we want

    Event.findByIdAndUpdate(req.params.id, req.body, function(err, foundEvent) {
       if(err) {
           console.log(err);
           req.flash("error", err);
       } else {
           req.flash("success", "Successfully edited your event");
       }
       res.redirect("/events/" + req.params.id);
    });
});

好吧,太乱了。

任何更好的解决方案将不胜感激