如何防止编辑表单时上传的图片被删除?
How to prevent uploaded image from getting deleted when editing a form?
我正在开发一款允许用户 post 活动的应用程序。我正在使用 NodeJS、Express 和 Mongo.
我创建了一个表单,允许用户输入活动详细信息,并上传与活动相关的图片。我还创建了一个允许用户编辑事件详细信息的表单。
表格如下所示:
问题:
- 用户用事件详细信息填写表单并附上图片。
- 用户提交表单
- 用户决定他想更改事件标题,但没有别的
- 用户点击编辑事件,更改标题,然后提交
- 问题:即使用户没有删除与事件关联的图片,该图片也不再存在。
这是我的 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);
});
});
好吧,太乱了。
任何更好的解决方案将不胜感激
我正在开发一款允许用户 post 活动的应用程序。我正在使用 NodeJS、Express 和 Mongo.
我创建了一个表单,允许用户输入活动详细信息,并上传与活动相关的图片。我还创建了一个允许用户编辑事件详细信息的表单。
表格如下所示:
问题:
- 用户用事件详细信息填写表单并附上图片。
- 用户提交表单
- 用户决定他想更改事件标题,但没有别的
- 用户点击编辑事件,更改标题,然后提交
- 问题:即使用户没有删除与事件关联的图片,该图片也不再存在。
这是我的 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);
});
});
好吧,太乱了。
任何更好的解决方案将不胜感激