ASP.NET AJAX AjaxFileUpload:引发上传完成事件并开始新上传的未捕获错误
ASP.NET AJAX AjaxFileUpload: Uncaught error raising upload complete event and start new upload
我已将 AJAX 多文件上传控件添加到我的 Web 应用程序。当我尝试上传多个文件时,只有第一个文件被上传并且 Chrome 开发控制台中显示以下错误:
Uncaught error raising upload complete event and start new upload
我的控制:
<cc1:AjaxFileUpload ID="multiUploader" ClientIDMode="Static" ClearFileListAfterUpload="false" OnClientUploadComplete="MultiUploadComplete" OnClientUploadCompleteAll="AllUploaded" OnUploadComplete="multiUploader_UploadComplete" MaximumNumberOfFiles="10" AllowedFileTypes="jpg,jpeg,png,gif" runat="server" />
JavaScript:
function MultiUploadComplete(sender, args) {
var filesize = args.get_fileSize();
var fileId = args.get_fileId();
var status = document.getElementById('multiUploader_FileItemStatus_' + fileId);
var container = document.getElementById('multiUploader_FileInfoContainer_' + fileId);
var fileName = $(container).find('span.filename').html();
if (filesize > 10240000) {
fileErrors += 1;
if (status.innerText) {
status.innerText = " (Error) " + $('#profilephotosize').attr('data-val').replace('{0}', fileName);
}
if (status.textContent) {
status.textContent = " (Error) " + $('#profilephotosize').attr('data-val').replace('{0}', fileName);
}
container.style.color = 'Red';
}
}
function AllUploaded() {
if (fileErrors > 0) {
alert($('#filesnotuploaded').attr('data-val').replace('{0}', fileErrors));
}
ShowData();
}
后端代码:
Private Function ResizeAndSaveImage(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal path As String, ByVal img As Image) As Boolean
'scale the image to maxWidth and maxHeight
'save image
Dim newWidth, newHeight As Integer
Dim scaleFactor As Double
Dim bResult As Boolean
newWidth = img.Width
newHeight = img.Height
If img.Width > maxWidth Or img.Height > maxHeight Then
If img.Width > maxWidth Then
scaleFactor = maxWidth / img.Width
newWidth = Math.Round(img.Width * scaleFactor, 0)
newHeight = Math.Round(img.Height * scaleFactor, 0)
End If
If newHeight > maxHeight Then
scaleFactor = maxHeight / newHeight
newWidth = Math.Round(newWidth * scaleFactor, 0)
newHeight = Math.Round(newHeight * scaleFactor, 0)
End If
End If
'code below copied from: http://www.webcosmoforums.com/asp/321-create-high-quality-thumbnail-resize-image-dynamically-asp-net-c-code.html
Try
Dim bmp As New Bitmap(newWidth, newHeight)
Dim gr As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High
Dim rectDestination As New System.Drawing.Rectangle(0, 0, newWidth, newHeight)
gr.DrawImage(img, rectDestination, 0, 0, img.Width, img.Height,
GraphicsUnit.Pixel)
bmp.Save(path)
System.IO.File.WriteAllBytes(ChangeExtension(path, "webp"), imageFunctions.EncodeImageToWebP(bmp))
bmp.Dispose()
bResult = True
Catch ex As Exception
End Try
Return bResult
End Function
Protected Sub afuPhoto_UploadedComplete(ByVal sender As Object, ByVal e As AjaxControlToolkit.AsyncFileUploadEventArgs)
Dim afuPhoto As AjaxControlToolkit.AsyncFileUpload = CType(sender, AjaxControlToolkit.AsyncFileUpload)
Dim pageId As Integer = 9
If ConfigurationManager.AppSettings("isDevelopment") = "false" Then
pageId = 1
End If
Dim allowedPhotos As Integer = ConfigurationManager.AppSettings("page_photos")
Dim TA As New mysiteTableAdapters.sitephotoalbumsTableAdapter
Dim totalPhotos As Integer = TA.CountPhotosForPageId(pageId)
Session("pageid") = pageId
'ReportError("afuPhoto_UploadedComplete.pageId", pageId.ToString)
If totalPhotos >= allowedPhotos Then
Exit Sub
End If
'code by mitsbits
If afuPhoto.HasFile AndAlso e.State = AjaxControlToolkit.AsyncFileUploadState.Success Then
'only if images are from same location, have same name and are uploaded at time calculated below a conflict could arise...chance minimal!
Dim d As String = (DateTime.Now.Millisecond + DateTime.Now.Second).ToString
If UploadFile(afuPhoto, pageId, d) = GlobalFunctions.ResultType.Success Then
'if file upload successfull update database with photo
TA.Insert(pageId, pageId.ToString + "_" + d + "_" + MakeValidImageName(afuPhoto.FileName), DateTime.Now)
End If
End If
End Sub
Protected Function UploadFile(ByVal FU As AjaxControlToolkit.AsyncFileUpload, ByVal locationId As Integer, ByVal curdate As String) As GlobalFunctions.ResultType
Dim filename As String = MakeValidImageName(FU.PostedFile.FileName)
Dim result As GlobalFunctions.ResultType
Dim thumbgalleryPath, galleryPath, largethumbgalleryPath As String
galleryPath = ConfigurationManager.AppSettings("page_photospath").ToString
thumbgalleryPath = ConfigurationManager.AppSettings("page_thumbsphotospath").ToString
largethumbgalleryPath = ConfigurationManager.AppSettings("page_largethumbsphotospath").ToString
If FU.PostedFile.FileName IsNot Nothing And FU.PostedFile.ContentLength > 0 Then
If FU.PostedFile.ContentLength <= 10240000 Then '10MB
If Not filename.ToLower.Contains(".jpg") And Not filename.ToLower.Contains(".png") And Not filename.ToLower.Contains(".gif") And Not filename.ToLower.Contains(".jpeg") Then
Return ResultType.notallowed
End If
Dim imgOriginal As Image = System.Drawing.Image.FromStream(FU.PostedFile.InputStream)
Dim imgOriginal1 As Image = imgOriginal 'this image is used as image is disposed
'save large image and save thumb image
If ResizeAndSaveImage(1200, 1200, Server.MapPath(galleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) And
ResizeAndSaveImage(450, 400, Server.MapPath(largethumbgalleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) And
ResizeAndSaveImage(75, 75, Server.MapPath(thumbgalleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) Then
result = ResultType.Success
End If
imgOriginal.Dispose()
End If
End If
If Not result = GlobalFunctions.ResultType.Success Then
'upload failed rollback
GlobalFunctions.DeleteFile(Server.MapPath(galleryPath) + locationId.ToString + "_" + filename)
GlobalFunctions.DeleteFile(Server.MapPath(largethumbgalleryPath) + locationId.ToString + "_" + filename)
GlobalFunctions.DeleteFile(Server.MapPath(thumbgalleryPath) + locationId.ToString + "_" + filename)
End If
Return result
End Function
Protected Sub multiUploader_UploadComplete(sender As Object, e As AjaxControlToolkit.AjaxFileUploadEventArgs) 'Handles multiUploader.UploadComplete
'check if the postback control was the linkbutton 'Opslaan', if it is, abort the upload function
'Dim ctrlname As String = Page.Request.Params.Get("__EVENTTARGET") 'PS. this code does NOT work when checking for postbacks with BUTTON controls
'If ctrlname.ToString <> "" AndAlso (ctrlname.Contains("btnSaveProfile") Or ctrlname.Contains("lbtnSetCoverPhoto")) Then 'save location button was clicked
' Exit Sub
'End If
'ReportError("multiUploader_UploadComplete")
Dim pageId = 1
Dim TAphotos As New mysiteTableAdapters.sitephotoalbumsTableAdapter
Dim DTphotos As mysite.sitephotoalbumsDataTable = TAphotos.GetPagePhotos(pageId)
Dim allowedPhotos As Integer = ConfigurationManager.AppSettings("page_photos")
Dim totalPhotos As Integer = DTphotos.Rows.Count
If totalPhotos >= allowedPhotos Then Exit Sub
'code by mitsbits
If e.State = AjaxControlToolkit.AjaxFileUploadState.Success Then 'multiUploader.afuPhoto.HasFile AndAlso
'only if images are from same location, have same name and are uploaded at time calculated below a conflict could arise...chance minimal!
'Dim d As String = (DateTime.Now.Millisecond + DateTime.Now.Second).ToString
Dim d As String = DateTime.Now.Second.ToString + "_" + DateTime.Now.Millisecond.ToString
Dim imageWidth, imageHeight As Integer
If e.FileSize > 10240000 Then
ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
"alert('file too large');", True)
Exit Sub
End If
'ReportError("e.filename", e.FileName.ToString)
Dim result As GlobalFunctions.ResultType = UploadMultiFile(System.Drawing.Image.FromStream(e.GetStreamContents), e.FileName, pageId, d, imageWidth, imageHeight)
If result = GlobalFunctions.ResultType.Success Then
'if file upload successfull update database with photo
Dim returnval As Integer
'Dim clientIPAddress As String = Request.ServerVariables("REMOTE_ADDR").ToString
returnval = TAphotos.Insert(pageId, pageId.ToString + "_" + d + "_" + MakeValidImageName(e.FileName), DateTime.Now)
ElseIf result = ResultType.invalidtype Then
' ' ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
' '"window.parent.$find('" + multiUploader.ClientID + "').newFileName='invalidfiletype';", True)
' Try
' ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
'"window.parent.$find('" + CType(dvSitePage.FindControl("multiUploader"), AjaxControlToolkit.AjaxFileUpload).ClientID + "').newFileName='invalidfiletype';", True)
' Catch ex As Exception
' End Try
End If
End If
End Sub
Protected Function UploadMultiFile(ByVal imgOriginal As Image, ByVal fileName As String, ByVal pageId As Integer, ByVal curdate As String, ByRef imageWidth As Integer, ByRef imageHeight As Integer) As GlobalFunctions.ResultType
fileName = MakeValidImageName(fileName)
Dim result As GlobalFunctions.ResultType
Dim thumbgalleryPath, origgalleryPath, photopathMedium As String
origgalleryPath = ConfigurationManager.AppSettings("page_photospath").ToString
photopathMedium = ConfigurationManager.AppSettings("page_largethumbsphotospath").ToString
thumbgalleryPath = ConfigurationManager.AppSettings("page_thumbsphotospath").ToString
Dim imgOriginal1 As Image = imgOriginal 'this image is used as image is disposed
'save large image and save thumb image
If ResizeAndSaveImage(1200, 1200, Server.MapPath(origgalleryPath) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) And', imageWidth, imageHeight
ResizeAndSaveImage(190, 190, Server.MapPath(photopathMedium) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) And
ResizeAndSaveImage(75, 75, Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) Then
result = ResultType.Success
End If
imgOriginal.Dispose()
If Not result = GlobalFunctions.ResultType.Success Then
'upload failed rollback
GlobalFunctions.DeleteFile(Server.MapPath(origgalleryPath) + pageId.ToString + "_" + fileName)
GlobalFunctions.DeleteFile(Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + fileName)
GlobalFunctions.DeleteFile(Server.MapPath(photopathMedium) + pageId.ToString + "_" + fileName)
GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(origgalleryPath) + pageId.ToString + "_" + fileName, "webp"))
GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + fileName, "webp"))
GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(photopathMedium) + pageId.ToString + "_" + fileName, "webp"))
Else
'update successfull
End If
Return result
End Function
我分析了“网络”选项卡:
并且有 3 个请求,最后一个以某种方式失败:
请求 1
姓名:https://www.example.com/account/my-site?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&start=1&queue=2&
状态:200
发起人:https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a
请求 2
姓名:https://www.example.com/AjaxFileUploadHandler.axd?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&fileId=AF96CF7B-B0EF-A47F-36E6-4E75CABB28D8&fileName=tt-ftp-settings.jpg&chunked=false&firstChunk=true
状态:200
发起人:https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a
请求 3
姓名:https://www.example.com/account/my-site?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&done=1&guid=AF96CF7B-B0EF-A47F-36E6-4E75CABB28D8&
状态:500
发起人:https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a
也在此处进行了检查,但这也与 AjaxFileUploadHandler.axd 有关,而不与 ScriptResource.axd 有关。
https://github.com/DevExpress/AjaxControlToolkit/issues/82
我将 sridharnethato 对 AjaxFileUploadHandler.axd 的回答添加到我的 web.config:AjaxControlToolkit: error raising upload complete event and start new upload
还尝试了此线程中的其他答案。
我当前的问题与ScriptResource.axd
有关,但我不知道为什么请求 1 成功,而请求 3 却不成功,尽管它们看起来几乎相同。
如何解决上述错误?
更新 1
开始测试来自@Greg 的答案,但我在函数 SaveImage
上遇到错误:{Message: "An error has occurred.", ExceptionMessage: "A generic error occurred in GDI+.",…}
更新 2
发现问题,显然 MapPath
正在将路由插入到文件路径中?有什么办法可以防止这种情况?这是我的定义:
<Route("api2/UploadFile/")>
Function UploadFile() As System.Web.Http.IHttpActionResult
Dim httpRequest = HttpContext.Current.Request
If ValidateImage(httpRequest.Files) Then
Dim file = httpRequest.Files(0)
Dim fileName = Path.GetFileName(file.FileName)
Dim galleryPath As String = ConfigurationManager.AppSettings("page_photospath").ToString
Dim filePath As String = HttpContext.Current.Server.MapPath(galleryPath) + fileName
===> HERE filePath IS: "C:\inetpub\example\api2\images\photos\Catan.jpg" where it should be: "C:\inetpub\example\images\photos\Catan.jpg"
Apparently `MapPath` is inserting the route into the path? Any way to prevent this?
SaveImage(file, filePath)
Return Ok()
Else
Return BadRequest()
End If
End Function
500 错误表示已向 Web 服务器发出请求,但它无法处理该请求。找到根本原因将很棘手,因为问题可能是前端、后端、环境(web.config/应用程序池设置)或 DevExpress 问题。
因此您应该 运行 隔离代码。例如:创建一个空白的 aspx 页面(无母版页),其中包含 AjaxFileUpload 并且有空 function/methods。如果可行,则添加最少的代码来保存图像(到临时文件夹)。如果可行,则逐位复制代码,直到可以创建错误。
或者在您的后端代码中添加一些日志记录。看起来你有一个隐藏异常的 try/catch 。如果出现错误,则变量 results 返回为 null。看起来 MultiUploadComplete() 不检查 成功 ,而只检查文件的大小?
或者重新编写 AjaxFileUpload。它没有得到很好的支持,现代 JavaScript、HTML 和网络浏览器意味着它更容易实现。
看看https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/。本文介绍如何使用纯 JavaScript、HTML5 创建多文件拖放上传器。您需要创建一个端点,最好使用 WebApi 或通用处理程序(如果使用 asp.net 的旧版本)
作为一个简单的概念验证。我制作了一个 aspx 页面,其中 post 个图像为 api。它一次单独发送文件以提高性能(但您可以作为一个请求完成所有操作)。需要指出的主要一点是,没有第三方库,您可以灵活地更改文件上传器的外观。
ASPX代码
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="WebForms._default" %>
<!DOCTYPE html>
<html lang="en">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div id="drop-area">
<p>Drop files here</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">Select files</label>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery" /></div>
<input id="btnUpload" type="submit" value="Upload" />
</form>
<script type="text/javascript">
let btnUpload = document.getElementById("btnUpload")
btnUpload.addEventListener('click', uploadFiles, false)
function uploadFiles(event) {
event.preventDefault();
// TODO - validate file size, extension & amount
files = [...fileElem.files]
// Submit each file separately.
files.forEach(uploadFile)
}
// This all copy & paste
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for (let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
//console.log('update', fileNumber, percent, total)
progressBar.value = total
return total === 100;
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
//files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function () {
let img = document.createElement('img')
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var url = '/api/UploadFile' // TODO: change end point
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function (e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
if (updateProgress(i, 100)) {
alert('Complete') // TODO
}
}
else if (xhr.readyState == 4 && xhr.status != 200) {
alert('error') // TODO
}
})
formData.append('file', file)
xhr.send(formData)
}
</script>
<style type="text/css">
body {
font-family: sans-serif;
}
a {
color: #369;
}
.note {
width: 500px;
margin: 50px auto;
font-size: 1.1em;
color: #333;
text-align: justify;
}
#drop-area {
border: 2px dashed #ccc;
border-radius: 20px;
width: 480px;
margin: 50px auto;
padding: 20px;
}
#drop-area.highlight {
border-color: purple;
}
p {
margin-top: 0;
}
.my-form {
margin-bottom: 10px;
}
#gallery {
margin-top: 10px;
}
#gallery img {
width: 150px;
margin-bottom: 10px;
margin-right: 10px;
vertical-align: middle;
}
.button {
display: inline-block;
padding: 10px;
background: #ccc;
cursor: pointer;
border-radius: 5px;
border: 1px solid #ccc;
}
.button:hover {
background: #ddd;
}
#fileElem {
display: none;
}
</style>
</body>
</html>
Web API 2.1(我选择了 C#,因为我接触 VB 年 )
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Http;
namespace WebForms.Controllers
{
public class UploadFilesController : ApiController
{
[Route("api/UploadFile/")]
public IHttpActionResult UploadFile()
{
var httpRequest = HttpContext.Current.Request;
if (ValidateImage(httpRequest.Files))
{
var file = httpRequest.Files[0];
var fileName = Path.GetFileName(file.FileName);
var filePath = HttpContext.Current.Server.MapPath($"~/{fileName}");
SaveImage(file, filePath);
return Ok();
}
else
{
return BadRequest();
}
}
private void SaveImage(HttpPostedFile file, string filePath)
{
using (var sourceimage = Image.FromStream(file.InputStream))
{
sourceimage.Save(filePath);
}
}
private bool ValidateImage(HttpFileCollection files)
{
return IsSingleFile(files)
&& IsValidFileType(files[0].FileName, files[0].ContentType)
&& IsValidSize(files[0].ContentLength);
}
public bool IsSingleFile(HttpFileCollection files)
{
return files.Count == 1;
}
public bool IsValidFileType(string fileName, string contentType)
{
string[] extensions = { "jpg", "jpeg", "gif", "bmp", "png" };
return extensions.Any(extension => $"image/{extension}".Equals(contentType, StringComparison.OrdinalIgnoreCase))
&& extensions.Any(extension => $".{extension}".Equals(Path.GetExtension(fileName.ToLower()), StringComparison.OrdinalIgnoreCase));
}
public bool IsValidSize(int contentLength)
{
return contentLength < 2100000; // 2MBs
}
}
}
我已将 AJAX 多文件上传控件添加到我的 Web 应用程序。当我尝试上传多个文件时,只有第一个文件被上传并且 Chrome 开发控制台中显示以下错误:
Uncaught error raising upload complete event and start new upload
我的控制:
<cc1:AjaxFileUpload ID="multiUploader" ClientIDMode="Static" ClearFileListAfterUpload="false" OnClientUploadComplete="MultiUploadComplete" OnClientUploadCompleteAll="AllUploaded" OnUploadComplete="multiUploader_UploadComplete" MaximumNumberOfFiles="10" AllowedFileTypes="jpg,jpeg,png,gif" runat="server" />
JavaScript:
function MultiUploadComplete(sender, args) {
var filesize = args.get_fileSize();
var fileId = args.get_fileId();
var status = document.getElementById('multiUploader_FileItemStatus_' + fileId);
var container = document.getElementById('multiUploader_FileInfoContainer_' + fileId);
var fileName = $(container).find('span.filename').html();
if (filesize > 10240000) {
fileErrors += 1;
if (status.innerText) {
status.innerText = " (Error) " + $('#profilephotosize').attr('data-val').replace('{0}', fileName);
}
if (status.textContent) {
status.textContent = " (Error) " + $('#profilephotosize').attr('data-val').replace('{0}', fileName);
}
container.style.color = 'Red';
}
}
function AllUploaded() {
if (fileErrors > 0) {
alert($('#filesnotuploaded').attr('data-val').replace('{0}', fileErrors));
}
ShowData();
}
后端代码:
Private Function ResizeAndSaveImage(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal path As String, ByVal img As Image) As Boolean
'scale the image to maxWidth and maxHeight
'save image
Dim newWidth, newHeight As Integer
Dim scaleFactor As Double
Dim bResult As Boolean
newWidth = img.Width
newHeight = img.Height
If img.Width > maxWidth Or img.Height > maxHeight Then
If img.Width > maxWidth Then
scaleFactor = maxWidth / img.Width
newWidth = Math.Round(img.Width * scaleFactor, 0)
newHeight = Math.Round(img.Height * scaleFactor, 0)
End If
If newHeight > maxHeight Then
scaleFactor = maxHeight / newHeight
newWidth = Math.Round(newWidth * scaleFactor, 0)
newHeight = Math.Round(newHeight * scaleFactor, 0)
End If
End If
'code below copied from: http://www.webcosmoforums.com/asp/321-create-high-quality-thumbnail-resize-image-dynamically-asp-net-c-code.html
Try
Dim bmp As New Bitmap(newWidth, newHeight)
Dim gr As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High
Dim rectDestination As New System.Drawing.Rectangle(0, 0, newWidth, newHeight)
gr.DrawImage(img, rectDestination, 0, 0, img.Width, img.Height,
GraphicsUnit.Pixel)
bmp.Save(path)
System.IO.File.WriteAllBytes(ChangeExtension(path, "webp"), imageFunctions.EncodeImageToWebP(bmp))
bmp.Dispose()
bResult = True
Catch ex As Exception
End Try
Return bResult
End Function
Protected Sub afuPhoto_UploadedComplete(ByVal sender As Object, ByVal e As AjaxControlToolkit.AsyncFileUploadEventArgs)
Dim afuPhoto As AjaxControlToolkit.AsyncFileUpload = CType(sender, AjaxControlToolkit.AsyncFileUpload)
Dim pageId As Integer = 9
If ConfigurationManager.AppSettings("isDevelopment") = "false" Then
pageId = 1
End If
Dim allowedPhotos As Integer = ConfigurationManager.AppSettings("page_photos")
Dim TA As New mysiteTableAdapters.sitephotoalbumsTableAdapter
Dim totalPhotos As Integer = TA.CountPhotosForPageId(pageId)
Session("pageid") = pageId
'ReportError("afuPhoto_UploadedComplete.pageId", pageId.ToString)
If totalPhotos >= allowedPhotos Then
Exit Sub
End If
'code by mitsbits
If afuPhoto.HasFile AndAlso e.State = AjaxControlToolkit.AsyncFileUploadState.Success Then
'only if images are from same location, have same name and are uploaded at time calculated below a conflict could arise...chance minimal!
Dim d As String = (DateTime.Now.Millisecond + DateTime.Now.Second).ToString
If UploadFile(afuPhoto, pageId, d) = GlobalFunctions.ResultType.Success Then
'if file upload successfull update database with photo
TA.Insert(pageId, pageId.ToString + "_" + d + "_" + MakeValidImageName(afuPhoto.FileName), DateTime.Now)
End If
End If
End Sub
Protected Function UploadFile(ByVal FU As AjaxControlToolkit.AsyncFileUpload, ByVal locationId As Integer, ByVal curdate As String) As GlobalFunctions.ResultType
Dim filename As String = MakeValidImageName(FU.PostedFile.FileName)
Dim result As GlobalFunctions.ResultType
Dim thumbgalleryPath, galleryPath, largethumbgalleryPath As String
galleryPath = ConfigurationManager.AppSettings("page_photospath").ToString
thumbgalleryPath = ConfigurationManager.AppSettings("page_thumbsphotospath").ToString
largethumbgalleryPath = ConfigurationManager.AppSettings("page_largethumbsphotospath").ToString
If FU.PostedFile.FileName IsNot Nothing And FU.PostedFile.ContentLength > 0 Then
If FU.PostedFile.ContentLength <= 10240000 Then '10MB
If Not filename.ToLower.Contains(".jpg") And Not filename.ToLower.Contains(".png") And Not filename.ToLower.Contains(".gif") And Not filename.ToLower.Contains(".jpeg") Then
Return ResultType.notallowed
End If
Dim imgOriginal As Image = System.Drawing.Image.FromStream(FU.PostedFile.InputStream)
Dim imgOriginal1 As Image = imgOriginal 'this image is used as image is disposed
'save large image and save thumb image
If ResizeAndSaveImage(1200, 1200, Server.MapPath(galleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) And
ResizeAndSaveImage(450, 400, Server.MapPath(largethumbgalleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) And
ResizeAndSaveImage(75, 75, Server.MapPath(thumbgalleryPath) + locationId.ToString + "_" + curdate + "_" + MakeValidImageName(filename), imgOriginal) Then
result = ResultType.Success
End If
imgOriginal.Dispose()
End If
End If
If Not result = GlobalFunctions.ResultType.Success Then
'upload failed rollback
GlobalFunctions.DeleteFile(Server.MapPath(galleryPath) + locationId.ToString + "_" + filename)
GlobalFunctions.DeleteFile(Server.MapPath(largethumbgalleryPath) + locationId.ToString + "_" + filename)
GlobalFunctions.DeleteFile(Server.MapPath(thumbgalleryPath) + locationId.ToString + "_" + filename)
End If
Return result
End Function
Protected Sub multiUploader_UploadComplete(sender As Object, e As AjaxControlToolkit.AjaxFileUploadEventArgs) 'Handles multiUploader.UploadComplete
'check if the postback control was the linkbutton 'Opslaan', if it is, abort the upload function
'Dim ctrlname As String = Page.Request.Params.Get("__EVENTTARGET") 'PS. this code does NOT work when checking for postbacks with BUTTON controls
'If ctrlname.ToString <> "" AndAlso (ctrlname.Contains("btnSaveProfile") Or ctrlname.Contains("lbtnSetCoverPhoto")) Then 'save location button was clicked
' Exit Sub
'End If
'ReportError("multiUploader_UploadComplete")
Dim pageId = 1
Dim TAphotos As New mysiteTableAdapters.sitephotoalbumsTableAdapter
Dim DTphotos As mysite.sitephotoalbumsDataTable = TAphotos.GetPagePhotos(pageId)
Dim allowedPhotos As Integer = ConfigurationManager.AppSettings("page_photos")
Dim totalPhotos As Integer = DTphotos.Rows.Count
If totalPhotos >= allowedPhotos Then Exit Sub
'code by mitsbits
If e.State = AjaxControlToolkit.AjaxFileUploadState.Success Then 'multiUploader.afuPhoto.HasFile AndAlso
'only if images are from same location, have same name and are uploaded at time calculated below a conflict could arise...chance minimal!
'Dim d As String = (DateTime.Now.Millisecond + DateTime.Now.Second).ToString
Dim d As String = DateTime.Now.Second.ToString + "_" + DateTime.Now.Millisecond.ToString
Dim imageWidth, imageHeight As Integer
If e.FileSize > 10240000 Then
ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
"alert('file too large');", True)
Exit Sub
End If
'ReportError("e.filename", e.FileName.ToString)
Dim result As GlobalFunctions.ResultType = UploadMultiFile(System.Drawing.Image.FromStream(e.GetStreamContents), e.FileName, pageId, d, imageWidth, imageHeight)
If result = GlobalFunctions.ResultType.Success Then
'if file upload successfull update database with photo
Dim returnval As Integer
'Dim clientIPAddress As String = Request.ServerVariables("REMOTE_ADDR").ToString
returnval = TAphotos.Insert(pageId, pageId.ToString + "_" + d + "_" + MakeValidImageName(e.FileName), DateTime.Now)
ElseIf result = ResultType.invalidtype Then
' ' ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
' '"window.parent.$find('" + multiUploader.ClientID + "').newFileName='invalidfiletype';", True)
' Try
' ScriptManager.RegisterClientScriptBlock(Me, Me.GetType(), "newfile",
'"window.parent.$find('" + CType(dvSitePage.FindControl("multiUploader"), AjaxControlToolkit.AjaxFileUpload).ClientID + "').newFileName='invalidfiletype';", True)
' Catch ex As Exception
' End Try
End If
End If
End Sub
Protected Function UploadMultiFile(ByVal imgOriginal As Image, ByVal fileName As String, ByVal pageId As Integer, ByVal curdate As String, ByRef imageWidth As Integer, ByRef imageHeight As Integer) As GlobalFunctions.ResultType
fileName = MakeValidImageName(fileName)
Dim result As GlobalFunctions.ResultType
Dim thumbgalleryPath, origgalleryPath, photopathMedium As String
origgalleryPath = ConfigurationManager.AppSettings("page_photospath").ToString
photopathMedium = ConfigurationManager.AppSettings("page_largethumbsphotospath").ToString
thumbgalleryPath = ConfigurationManager.AppSettings("page_thumbsphotospath").ToString
Dim imgOriginal1 As Image = imgOriginal 'this image is used as image is disposed
'save large image and save thumb image
If ResizeAndSaveImage(1200, 1200, Server.MapPath(origgalleryPath) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) And', imageWidth, imageHeight
ResizeAndSaveImage(190, 190, Server.MapPath(photopathMedium) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) And
ResizeAndSaveImage(75, 75, Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + curdate + "_" + fileName, imgOriginal) Then
result = ResultType.Success
End If
imgOriginal.Dispose()
If Not result = GlobalFunctions.ResultType.Success Then
'upload failed rollback
GlobalFunctions.DeleteFile(Server.MapPath(origgalleryPath) + pageId.ToString + "_" + fileName)
GlobalFunctions.DeleteFile(Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + fileName)
GlobalFunctions.DeleteFile(Server.MapPath(photopathMedium) + pageId.ToString + "_" + fileName)
GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(origgalleryPath) + pageId.ToString + "_" + fileName, "webp"))
GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(thumbgalleryPath) + pageId.ToString + "_" + fileName, "webp"))
GlobalFunctions.DeleteFile(IO.Path.ChangeExtension(Server.MapPath(photopathMedium) + pageId.ToString + "_" + fileName, "webp"))
Else
'update successfull
End If
Return result
End Function
我分析了“网络”选项卡:
并且有 3 个请求,最后一个以某种方式失败:
请求 1
姓名:https://www.example.com/account/my-site?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&start=1&queue=2&
状态:200
发起人:https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a
请求 2
姓名:https://www.example.com/AjaxFileUploadHandler.axd?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&fileId=AF96CF7B-B0EF-A47F-36E6-4E75CABB28D8&fileName=tt-ftp-settings.jpg&chunked=false&firstChunk=true
状态:200
发起人:https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a
请求 3
姓名:https://www.example.com/account/my-site?contextKey={DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}&controlID=multiUploader&done=1&guid=AF96CF7B-B0EF-A47F-36E6-4E75CABB28D8&
状态:500
发起人:https://www.example.com/ScriptResource.axd?d=-9YDjdHizPInlZIfdnhPn9wQrtV-icwCIGM6rMTgL1xcc9eo0V8JJ8oN6GiCmQReZbL-gv3nU-BhwRv3l8r5gubGD2yQ03ZVzdvO2Ko-nvG4Lmxrd4NQGjsi7m-ARIjq0&t=7d5986a
也在此处进行了检查,但这也与 AjaxFileUploadHandler.axd 有关,而不与 ScriptResource.axd 有关。 https://github.com/DevExpress/AjaxControlToolkit/issues/82
我将 sridharnethato 对 AjaxFileUploadHandler.axd 的回答添加到我的 web.config:AjaxControlToolkit: error raising upload complete event and start new upload 还尝试了此线程中的其他答案。
我当前的问题与ScriptResource.axd
有关,但我不知道为什么请求 1 成功,而请求 3 却不成功,尽管它们看起来几乎相同。
如何解决上述错误?
更新 1
开始测试来自@Greg 的答案,但我在函数 SaveImage
上遇到错误:{Message: "An error has occurred.", ExceptionMessage: "A generic error occurred in GDI+.",…}
更新 2
发现问题,显然 MapPath
正在将路由插入到文件路径中?有什么办法可以防止这种情况?这是我的定义:
<Route("api2/UploadFile/")>
Function UploadFile() As System.Web.Http.IHttpActionResult
Dim httpRequest = HttpContext.Current.Request
If ValidateImage(httpRequest.Files) Then
Dim file = httpRequest.Files(0)
Dim fileName = Path.GetFileName(file.FileName)
Dim galleryPath As String = ConfigurationManager.AppSettings("page_photospath").ToString
Dim filePath As String = HttpContext.Current.Server.MapPath(galleryPath) + fileName
===> HERE filePath IS: "C:\inetpub\example\api2\images\photos\Catan.jpg" where it should be: "C:\inetpub\example\images\photos\Catan.jpg"
Apparently `MapPath` is inserting the route into the path? Any way to prevent this?
SaveImage(file, filePath)
Return Ok()
Else
Return BadRequest()
End If
End Function
500 错误表示已向 Web 服务器发出请求,但它无法处理该请求。找到根本原因将很棘手,因为问题可能是前端、后端、环境(web.config/应用程序池设置)或 DevExpress 问题。
因此您应该 运行 隔离代码。例如:创建一个空白的 aspx 页面(无母版页),其中包含 AjaxFileUpload 并且有空 function/methods。如果可行,则添加最少的代码来保存图像(到临时文件夹)。如果可行,则逐位复制代码,直到可以创建错误。
或者在您的后端代码中添加一些日志记录。看起来你有一个隐藏异常的 try/catch 。如果出现错误,则变量 results 返回为 null。看起来 MultiUploadComplete() 不检查 成功 ,而只检查文件的大小?
或者重新编写 AjaxFileUpload。它没有得到很好的支持,现代 JavaScript、HTML 和网络浏览器意味着它更容易实现。
看看https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/。本文介绍如何使用纯 JavaScript、HTML5 创建多文件拖放上传器。您需要创建一个端点,最好使用 WebApi 或通用处理程序(如果使用 asp.net 的旧版本)
作为一个简单的概念验证。我制作了一个 aspx 页面,其中 post 个图像为 api。它一次单独发送文件以提高性能(但您可以作为一个请求完成所有操作)。需要指出的主要一点是,没有第三方库,您可以灵活地更改文件上传器的外观。
ASPX代码
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="WebForms._default" %>
<!DOCTYPE html>
<html lang="en">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div id="drop-area">
<p>Drop files here</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">Select files</label>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery" /></div>
<input id="btnUpload" type="submit" value="Upload" />
</form>
<script type="text/javascript">
let btnUpload = document.getElementById("btnUpload")
btnUpload.addEventListener('click', uploadFiles, false)
function uploadFiles(event) {
event.preventDefault();
// TODO - validate file size, extension & amount
files = [...fileElem.files]
// Submit each file separately.
files.forEach(uploadFile)
}
// This all copy & paste
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for (let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
//console.log('update', fileNumber, percent, total)
progressBar.value = total
return total === 100;
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
//files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function () {
let img = document.createElement('img')
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var url = '/api/UploadFile' // TODO: change end point
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function (e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
if (updateProgress(i, 100)) {
alert('Complete') // TODO
}
}
else if (xhr.readyState == 4 && xhr.status != 200) {
alert('error') // TODO
}
})
formData.append('file', file)
xhr.send(formData)
}
</script>
<style type="text/css">
body {
font-family: sans-serif;
}
a {
color: #369;
}
.note {
width: 500px;
margin: 50px auto;
font-size: 1.1em;
color: #333;
text-align: justify;
}
#drop-area {
border: 2px dashed #ccc;
border-radius: 20px;
width: 480px;
margin: 50px auto;
padding: 20px;
}
#drop-area.highlight {
border-color: purple;
}
p {
margin-top: 0;
}
.my-form {
margin-bottom: 10px;
}
#gallery {
margin-top: 10px;
}
#gallery img {
width: 150px;
margin-bottom: 10px;
margin-right: 10px;
vertical-align: middle;
}
.button {
display: inline-block;
padding: 10px;
background: #ccc;
cursor: pointer;
border-radius: 5px;
border: 1px solid #ccc;
}
.button:hover {
background: #ddd;
}
#fileElem {
display: none;
}
</style>
</body>
</html>
Web API 2.1(我选择了 C#,因为我接触 VB 年 )
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Http;
namespace WebForms.Controllers
{
public class UploadFilesController : ApiController
{
[Route("api/UploadFile/")]
public IHttpActionResult UploadFile()
{
var httpRequest = HttpContext.Current.Request;
if (ValidateImage(httpRequest.Files))
{
var file = httpRequest.Files[0];
var fileName = Path.GetFileName(file.FileName);
var filePath = HttpContext.Current.Server.MapPath($"~/{fileName}");
SaveImage(file, filePath);
return Ok();
}
else
{
return BadRequest();
}
}
private void SaveImage(HttpPostedFile file, string filePath)
{
using (var sourceimage = Image.FromStream(file.InputStream))
{
sourceimage.Save(filePath);
}
}
private bool ValidateImage(HttpFileCollection files)
{
return IsSingleFile(files)
&& IsValidFileType(files[0].FileName, files[0].ContentType)
&& IsValidSize(files[0].ContentLength);
}
public bool IsSingleFile(HttpFileCollection files)
{
return files.Count == 1;
}
public bool IsValidFileType(string fileName, string contentType)
{
string[] extensions = { "jpg", "jpeg", "gif", "bmp", "png" };
return extensions.Any(extension => $"image/{extension}".Equals(contentType, StringComparison.OrdinalIgnoreCase))
&& extensions.Any(extension => $".{extension}".Equals(Path.GetExtension(fileName.ToLower()), StringComparison.OrdinalIgnoreCase));
}
public bool IsValidSize(int contentLength)
{
return contentLength < 2100000; // 2MBs
}
}
}