、說明
接上一篇文章uploadify實現多附件上傳完成后,又突然用到頭像上傳并在線裁剪。在網上找個眾多例子都沒有符合要求的,有一篇文章寫的不錯,就是文旺老兄寫的這篇Asp.Net平臺下的圖片在線裁剪功能的實現,大家可以看一下,寫的真不錯。我就是在參考了他的代碼下,結合uploadify使用一般處理程序實現了這個功能,寫下了這篇在asp.net實現圖片在線上傳并在線裁剪,有點繞口哈,廢話不多說,現奉上代碼,供同學們交流參考,有什么不對的地方,望請大家多多提提建議,多謝!多謝!
2、組成
首先說明一下代碼實現所用到的技術,僅供參考:
開發工具:vs2010
目標框架:.NET Framework3.5
jcrop:Jcrop.js v0.9.12
Uploadify:uploadify-v3.1
Jquery:jquery-1.9.0.js
最后我會將整個Demo上傳,如果同學們的電腦上有開發環境可直接打開項目解決方案運行。
3、代碼
Default.aspx(測試頁面)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ImgJcrop._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>在線裁剪</title>
<link href="Scripts/jcrop/jquery.Jcrop.css" rel="stylesheet" type="text/css" />
<link href="Scripts/uploadify-v3.1/uploadify.css" rel="stylesheet" type="text/css" />
<script src="Scripts/jquery.1.9.0.min.js" type="text/javascript"></script>
<script src="Scripts/jcrop/jquery.Jcrop.js" type="text/javascript"></script>
<script src="Scripts/uploadify-v3.1/jquery.uploadify-3.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
var jcrop_api, boundx, boundy;
$("#file_upload").uploadify({
"auto": true,
"buttonText": "選擇圖片",
"swf": "Scripts/uploadify-v3.1/uploadify.swf",
"uploader": "App_Handler/Uploadify.ashx?action=upload",
"fileTypeExts": "*.jpg; *.jpeg; *.gif; *.png; *.bmp",
"fileTypeDesc": "支持的格式:",
"multi": false,
"removeCompleted": false,
"onUploadStart": function (file) {
$("#file_upload-queue").hide();
},
"onUploadSuccess": function (file, data, response) {
var row=eval("[" + data + "]");
if (row[0]["status"]==0) {
$("#cutimg").html("<img id=\"imgOriginal\" name=\"imgOriginal\" /><div style=\"overflow: hidden; margin-top: 20px;\"><div style=\"width: 120px; height: 120px; overflow: hidden;\"><img id=\"imgPreview\" /></div><br /><input type=\"button\" id=\"btnImgCut\" onclick=\"cutSaveImg()\" value=\"裁剪并保存圖片\" /></div>");
$("#cutimg img").each(function () { $(this).attr("src", row[0]["message"]); });
$("#hidImgUrl").val(row[0]["message"]);
$('#imgOriginal').Jcrop({
onChange: updatePreview,
onSelect: updatePreview,
aspectRatio: 1,
//maxSize: [120, 120],
setSelect: [0, 0, 120, 120]
}, function () {
var bounds=this.getBounds();
boundx=bounds[0];
boundy=bounds[1];
jcrop_api=this;
});
} else {
alert(row[0]["message"]);
}
}
});
function updatePreview(c) {
if (parseInt(c.w) > 0) {
var rx=120 / c.w;
var ry=120 / c.h;
$("#imgPreview").css({
width: Math.round(rx * boundx) + "px",
height: Math.round(ry * boundy) + "px",
marginLeft: "-" + Math.round(rx * c.x) + "px",
marginTop: "-" + Math.round(ry * c.y) + "px"
});
}
$("#hidXone").val(c.x);
$("#hidYone").val(c.y);
$("#hidXtwo").val(c.hidXtwo);
$("#hidYtwo").val(c.hidYtwo);
$("#hidImgWidth").val(c.w);
$("#hidImgHeight").val(c.h);
};
});
function cutSaveImg() {
$.ajax({
type: "post",
url: "App_Handler/Uploadify.ashx?action=cutsaveimg",
data: { strImgUrl: $("#imgOriginal")[0].src, hidXone: $("#hidXone").val(), hidYone: $("#hidYone").val(), hidImgWidth: $("#hidImgWidth").val(), hidImgHeight: $("#hidImgHeight").val() },
dataType: "html",
success: function (data) {
var row=eval("[" + data + "]");
if (row[0]["status"]==0) { }
alert(row[0]["message"]);
}
});
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="file" id="file_upload" name="file_upload" />
</div>
<div id="cutimg">
</div>
<asp:HiddenField ID="hidXone" runat="server" />
<asp:HiddenField ID="hidYone" runat="server" />
<asp:HiddenField ID="hidXtwo" runat="server" />
<asp:HiddenField ID="hidYtwo" runat="server" />
<asp:HiddenField ID="hidImgWidth" runat="server" />
<asp:HiddenField ID="hidImgHeight" runat="server" />
<asp:HiddenField ID="hidImgUrl" runat="server" />
</form>
</body>
</html>
Uploadify.ashx(一般處理程序)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<%@ WebHandler Language="C#" Class="UploadifyUpload" %>
using System;
using System.Collections;
using System.Data;
using System.Web;
using System.Linq;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.SessionState;
using System.IO;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
public class UploadifyUpload : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType="text/plain";
context.Response.Charset="utf-8";
string action=context.Request["action"];
switch (action)
{
case "upload":
//上傳圖片
upload(context);
break;
case "cutsaveimg":
//裁剪并保存
cutsaveimg(context);
break;
}
context.Response.End();
}
/// <summary>
/// 上傳圖片
/// </summary>
/// <param name="context"></param>
private void upload(HttpContext context)
{
HttpPostedFile postedFile=context.Request.Files["Filedata"];
if (postedFile !=null)
{
string fileName, fileExtension;
int fileSize;
fileName=postedFile.FileName;
fileSize=postedFile.ContentLength;
if (fileName !="")
{
fileExtension=postedFile.FileName.Substring(postedFile.FileName.LastIndexOf('.'));
string strPath=context.Server.MapPath("/") + "\App_File\Upload\";//設置文件的路徑
string strFileName="upload" + DateTime.Now.ToString("yyyyMMddHHmmss") + fileExtension;
string strFileUrl=strPath + strFileName;//保存文件路徑
if (!Directory.Exists(strPath))
{
Directory.CreateDirectory(strPath);
}
postedFile.SaveAs(strFileUrl);//先保存源文件
context.Response.Write("{\"status\":0,\"message\":\"/App_File/Upload/" + strFileName + "\"}");
}
else
{
context.Response.Write("{\"status\":1,\"message\":\"上傳失敗!\"}");
}
}
else
{
context.Response.Write("{\"status\":1,\"message\":\"上傳失敗!\"}");
}
}
/// <summary>
/// 裁剪并保存圖片
/// </summary>
/// <param name="context"></param>
private void cutsaveimg(HttpContext context)
{
string strImgUrl=context.Request["strImgUrl"];
string strXone=context.Request["hidXone"];
string strYone=context.Request["hidYone"];
string strImgWidth=context.Request["hidImgWidth"];
string strImgHeight=context.Request["hidImgHeight"];
string[] urls=strImgUrl.Split('/');
string str_url=urls.Last();
try
{
string strOldFiel=context.Server.MapPath("~/App_File/Upload/");
string strNewFiel=context.Server.MapPath("~/App_File/Cut/");
string strOldUrl=Path.Combine(strOldFiel, str_url);
string strNewUrl=Path.Combine(strNewFiel, "cut" + DateTime.Now.ToString("yyyyMMddHHmmss") + "." + str_url.Split('.')[1]);
if (!Directory.Exists(strNewFiel))
{
Directory.CreateDirectory(strNewFiel);
}
int intStartX=int.Parse(strXone);
int intStartY=int.Parse(strYone);
int intWidth=int.Parse(strImgWidth);
int intHeight=int.Parse(strImgHeight);
CutGeneratedImage(intStartX, intStartY, intWidth, intHeight, strOldUrl, strNewUrl);
context.Response.Write("{\"status\":0,\"message\":\"裁剪成功并保存!\"}");
}
catch
{
context.Response.Write("{\"status\":1,\"message\":\"裁剪失敗!\"}");
}
}
/// <summary>
/// 裁剪圖片
/// </summary>
/// <param name="intWidth">要縮小裁剪圖片寬度</param>
/// <param name="intHeight">要縮小裁剪圖片長度</param>
/// <param name="strOldImgUrl">要處理圖片路徑</param>
/// <param name="strNewImgUrl">處理完畢圖片路徑</param>
public void CutGeneratedImage(int intStartX, int intStartY, int intWidth, int intHeight, string strOldImgUrl, string strNewImgUrl)
{
//上傳標準圖大小
int intStandardWidth=120;
int intStandardHeight=120;
int intReduceWidth=0; // 縮小的寬度
int intReduceHeight=0; // 縮小的高度
int intCutOutWidth=0; // 裁剪的寬度
int intCutOutHeight=0; // 裁剪的高度
int level=100; //縮略圖的質量 1-100的范圍
//獲得縮小,裁剪大小
if (intStandardHeight * intWidth / intStandardWidth > intHeight)
{
intReduceWidth=intWidth;
intReduceHeight=intStandardHeight * intWidth / intStandardWidth;
intCutOutWidth=intWidth;
intCutOutHeight=intHeight;
}
else if (intStandardHeight * intWidth / intStandardWidth < intHeight)
{
intReduceWidth=intStandardWidth * intHeight / intStandardHeight;
intReduceHeight=intHeight;
intCutOutWidth=intWidth;
intCutOutHeight=intHeight;
}
else
{
intReduceWidth=intWidth;
intReduceHeight=intHeight;
intCutOutWidth=intWidth;
intCutOutHeight=intHeight;
}
//通過連接創建Image對象
//System.Drawing.Image oldimage=System.Drawing.Image.FromFile(strOldImgUrl);
//oldimage.Save(Server.MapPath("tepm.jpg"));
//oldimage.Dispose();
//縮小圖片
Bitmap bm=new Bitmap(strOldImgUrl);
//處理JPG質量的函數
ImageCodecInfo[] codecs=ImageCodecInfo.GetImageEncoders();
ImageCodecInfo ici=null;
foreach (ImageCodecInfo codec in codecs)
{
if (codec.MimeType=="image/jpeg")
{
ici=codec;
break;
}
}
EncoderParameters ep=new EncoderParameters();
ep.Param[0]=new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)level);
//裁剪圖片
Rectangle cloneRect=new Rectangle(intStartX, intStartY, intCutOutWidth, intCutOutHeight);
PixelFormat format=bm.PixelFormat;
Bitmap cloneBitmap=bm.Clone(cloneRect, format);
//保存圖片
cloneBitmap.Save(strNewImgUrl, ici, ep);
bm.Dispose();
}
public bool IsReusable
{
get
{
return false;
}
}
}
4、最后奉上Demo
https://files.cnblogs.com/files/lengzhan/ImgJcrop.zip
作為程序員的我們,經常要用到文件的上傳和下載功能。到了需要用的時候,各種查資料。有木有..有木有...。為了方便下次使用,這里來做個總結和備忘。
最原始、最簡單、最粗暴的文件上傳。
前端代碼:
//方式1
<form action="/Home/SaveFile1" method="post" enctype="multipart/form-data">
<input type="file" class="file1" name="file1" />
<button type="submit" class="but1">上傳</button>
</form>
【注意】
后臺代碼:
public ActionResult SaveFile1()
{
if (Request.Files.Count > 0)
{
Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Request.Files[0].FileName);
return Content("保存成功");
}
return Content("沒有讀到文件");
}
雖然上面的方式簡單粗暴,但是不夠友好。頁面必然會刷新。難以實現停留在當前頁面,并給出文件上傳成功的提示。
隨著時間的流逝,技術日新月異。ajax的出現,使得異步文件提交變得更加容易。
下面我們利用jquery.form插件來實現文件的異步上傳。
首先我們需要導入jquery.js和jquery.form.js
前端代碼:
<form id="form2" action="/Home/SaveFile2" method="post" enctype="multipart/form-data">
<input type="file" class="file1" name="file1" />
<button type="submit" class="but1">上傳1</button>
<button type="button" class="but2">上傳2</button>
</form>
//方式2(通過ajaxForm綁定ajax操作)
$(function () {
$('#form2').ajaxForm({
success: function (responseText) {
alert(responseText);
}
});
});
//方式3(通過ajaxSubmit直接執行ajax操作)
$(function () {
$(".but2").click(function () {
$('#form2').ajaxSubmit({
success: function (responseText) {
alert(responseText);
}
});
});
});
后臺代碼:
public string SaveFile2()
{
if (Request.Files.Count > 0)
{
Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName));
return "保存成功";
}
return "沒有讀到文件";
}
原理:
我們很多時候使用了插件,就不管其他三七二十一呢。
如果有點好奇心,想想這個插件是怎么實現的。隨便看了看源碼一千五百多行。我的媽呀,不就是個異步上傳的嗎,怎么這么復雜。
難以看出個什么鬼來,直接斷點調試下吧。
原來插件內部有iframe和FormData不同方式來上傳,來適應更多版本瀏覽器。
iframe這東西太惡心。我們看到上面可以利用FormData來上傳文件,這個是Html 5 才有的。下面我們自己也來試試吧。
前端代碼:
<input id="fileinfo" type="file" class="notFormFile" />
<button type="button" class="btnNotForm">上傳4</button>
//方式4
$(".btnNotForm").click(function () {
var formData=new FormData();//初始化一個FormData對象
formData.append("files", $(".notFormFile")[0].files[0]);//將文件塞入FormData
$.ajax({
url: "/Home/SaveFile2",
type: "POST",
data: formData,
processData: false, // 告訴jQuery不要去處理發送的數據
contentType: false, // 告訴jQuery不要去設置Content-Type請求頭
success: function (responseText) {
alert(responseText);
}
});
});
后面的代碼:(不變,還是上例代碼)
public string SaveFile2()
{
if (Request.Files.Count > 0)
{
Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName));
return "保存成功";
}
return "沒有讀到文件";
}
我們看到,FormData對象也只是在模擬一個原始的表單格式的數據。那有沒有可能利用表單或表單格式來上傳文件呢?答案是肯定的。(下面馬上揭曉)
前端代碼:
<input type="file" id="file5" multiple>
<button type="button" class="btnFile5">上傳5</button>
//方式5
$(".btnFile5").click(function () {
$.ajax({
url: "/Home/SaveFile4",
type: "POST",
data: $("#file5")[0].files[0],
processData: false, // 告訴jQuery不要去處理發送的數據
contentType: false, // 告訴jQuery不要去設置Content-Type請求頭
success: function (responseText) {
alert(responseText);
}
});;
});
后臺代碼:
public string SaveFile4()
{
//這里發現只能得到一個網絡流,沒有其他信息了。(比如,文件大小、文件格式、文件名等)
Request.SaveAs(Server.MapPath("~/App_Data/SaveFile4.data") + "", false);
return "保存成功";
}
細心的你發現沒有了表單格式,我們除了可以上傳文件流數據外,不能再告訴后臺其他信息了(如文件格式)。
到這里,我似乎明白了以前上傳文件為什么非得要用個form包起來,原來這只是和后臺約定的一個傳輸格式而已。
其實我們單純地用jq的ajax傳輸文本數據的時候,最后也是組裝成了form格式的數據,如:
$.ajax({
data: { "userName": "張三" }
在知道了上面的各種上傳之后,我們是不是就滿于現狀了呢?no,很多時候我們需要傳輸大文件,一般服務器都會有一定的大小限制。
某天,你發現了一個激情小電影想要分享給大家。無奈,高清文件太大傳不了,怎么辦?我們可以化整為零,一部分一部分的傳嘛,也就是所謂的分片上傳。
前端代碼:
<input type="file" id="file6" multiple>
<button type="button" class="btnFile6">分片上傳6</button>
<div class="result"></div>
//方式6
$(".btnFile6").click(function () {
var upload=function (file, skip) {
var formData=new FormData();//初始化一個FormData對象
var blockSize=1000000;//每塊的大小
var nextSize=Math.min((skip + 1) * blockSize, file.size);//讀取到結束位置
var fileData=file.slice(skip * blockSize, nextSize);//截取 部分文件 塊
formData.append("file", fileData);//將 部分文件 塞入FormData
formData.append("fileName", file.name);//保存文件名字
$.ajax({
url: "/Home/SaveFile6",
type: "POST",
data: formData,
processData: false, // 告訴jQuery不要去處理發送的數據
contentType: false, // 告訴jQuery不要去設置Content-Type請求頭
success: function (responseText) {
$(".result").html("已經上傳了" + (skip + 1) + "塊文件");
if (file.size <=nextSize) {//如果上傳完成,則跳出繼續上傳
alert("上傳完成");
return;
}
upload(file, ++skip);//遞歸調用
}
});
};
var file=$("#file6")[0].files[0];
upload(file, 0);
});
后臺代碼:
public string SaveFile6()
{
//保存文件到根目錄 App_Data + 獲取文件名稱和格式
var filePath=Server.MapPath("~/App_Data/") + Request.Form["fileName"];
//創建一個追加(FileMode.Append)方式的文件流
using (FileStream fs=new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
using (BinaryWriter bw=new BinaryWriter(fs))
{
//讀取文件流
BinaryReader br=new BinaryReader(Request.Files[0].InputStream);
//將文件留轉成字節數組
byte[] bytes=br.ReadBytes((int)Request.Files[0].InputStream.Length);
//將字節數組追加到文件
bw.Write(bytes);
}
}
return "保存成功";
}
相對而言,代碼量多了一點,復雜了一點。不過相對于網上的其他分片上傳的代碼應該要簡單得多(因為這里沒有考慮多文件塊同時上傳、斷點續傳。那樣就需要在后臺把文件塊排序,然后上傳完成按序合并,然后刪除原來的臨時文件。有興趣的同學可以自己試試,稍候在分析上傳插件webuploader的時候也會實現)。
效果圖:
【說明】:如果我們想要上傳多個文件怎么辦?其實H5中也提供了非常簡單的方式。直接在input里面標記multiple,<input type="file" id="file6" multiple>,然后我們后臺接收的也是一個數組Request.Files。
只能說H5真是強大啊,權限越來越大,操作越來越牛逼。
前端代碼(拖拽上傳):
<textarea class="divFile7" style="min-width:800px;height:150px" placeholder="請將文件拖拽或直接粘貼到這里"></textarea>
//方式7
$(".divFile7")[0].ondrop=function (event) {
event.preventDefault();//不要執行與事件關聯的默認動作
var files=event.dataTransfer.files;//獲取拖上來的文件
//以下代碼不變
var formData=new FormData();//初始化一個FormData對象
formData.append("files", files[0]);//將文件塞入FormData
$.ajax({
url: "/Home/SaveFile2",
type: "POST",
data: formData,
processData: false, // 告訴jQuery不要去處理發送的數據
contentType: false, // 告訴jQuery不要去設置Content-Type請求頭
success: function (responseText) {
alert(responseText);
}
});
};
后臺代碼:
略(和之前的SaveFile2一樣)
前端代碼(粘貼上傳 限圖片格式):
//方式8
$(".divFile7")[0].onpaste=function (event) {
event.preventDefault();//不要執行與事件關聯的默認動作
var clipboard=event.clipboardData.items[0];//剪貼板數據
if (clipboard.kind=='file' || clipboard.type.indexOf('image') > -1) {//判斷是圖片格式
var imageFile=clipboard.getAsFile();//獲取文件
//以下代碼不變
var formData=new FormData;
formData.append('files', imageFile);
formData.append('fileName', "temp.png");//這里給文件命個名(或者直接在后臺保存的時候命名)
$.ajax({
url: "/Home/SaveFile8",
type: "POST",
data: formData,
processData: false, // 告訴jQuery不要去處理發送的數據
contentType: false, // 告訴jQuery不要去設置Content-Type請求頭
success: function (responseText) {
alert(responseText);
}
});
}
};
后臺代碼:
public string SaveFile8()
{
//保存文件到根目錄 App_Data + 獲取文件名稱和格式
var filePath=Server.MapPath("~/App_Data/") + Request.Form["fileName"];
if (Request.Files.Count > 0)
{
Request.Files[0].SaveAs(filePath);
return "保存成功";
}
return "沒有讀到文件";
}
效果圖:
已經列舉分析了多種上傳文件的方式,我想總有一種適合你。不過,上傳這個功能比較通用,而我們自己寫的可能好多情況沒有考慮到。接下來簡單介紹下百度的WebUploader插件。
比起我們自己寫的簡單上傳,它的優勢:穩定、兼容性好(有flash切換,所以支持IE)、功能多、并發上傳、斷點續傳(主要還是靠后臺配合)。
官網:http://fex.baidu.com/webuploader/
插件下載:https://github.com/fex-team/webuploader/releases/download/0.1.5/webuploader-0.1.5.zip
下面開始對WebUploader的使用
第一種,簡單粗暴
前端代碼:
<div id="picker">選擇文件</div>
<button id="ctlBtn" class="btn btn-default">開始上傳</button>
<!--引用webuploader的js和css-->
<link href="~/Scripts/webuploader-0.1.5/webuploader.css" rel="stylesheet" />
<script src="~/Scripts/webuploader-0.1.5/webuploader.js"></script>
<script type="text/javascript">
var uploader=WebUploader.create({
// (如果是新瀏覽器 可以不用 flash)
//swf: '/Scripts/webuploader-0.1.5/Uploader.swf',
// 文件接收服務端。
server: '/Webuploader/SaveFile',
// 選擇文件的按鈕。可選。
// 內部根據當前運行是創建,可能是input元素,也可能是flash.
pick: '#picker'
});
$("#ctlBtn").click(function () {
uploader.upload();
});
uploader.on('uploadSuccess', function (file) {
alert("上傳成功");
});
</script>
后臺代碼:
public string SaveFile()
{
if (Request.Files.Count > 0)
{
Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName));
return "保存成功";
}
return "沒有讀到文件";
}
第二種,分片上傳。和我們之前自己寫的效果差不多。
前端代碼:
var uploader=WebUploader.create({
//兼容老版本IE
swf: '/Scripts/webuploader-0.1.5/Uploader.swf',
// 文件接收服務端。
server: '/Webuploader/SveFile2',
// 開起分片上傳。
chunked: true,
//分片大小
chunkSize: 1000000,
//上傳并發數
threads: 1,
// 選擇文件的按鈕。
pick: '#picker'
});
// 點擊觸發上傳
$("#ctlBtn").click(function () {
uploader.upload();
});
uploader.on('uploadSuccess', function (file) {
alert("上傳成功");
});
后臺代碼:
public string SveFile2()
{
//保存文件到根目錄 App_Data + 獲取文件名稱和格式
var filePath=Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName);
//創建一個追加(FileMode.Append)方式的文件流
using (FileStream fs=new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
using (BinaryWriter bw=new BinaryWriter(fs))
{
//讀取文件流
BinaryReader br=new BinaryReader(Request.Files[0].InputStream);
//將文件留轉成字節數組
byte[] bytes=br.ReadBytes((int)Request.Files[0].InputStream.Length);
//將字節數組追加到文件
bw.Write(bytes);
}
}
return "保存成功";
}
我們看到了有個參數threads: 1上傳并發數,如果我們改成大于1會怎樣?前端會同時發起多個文件片上傳。后臺就會報錯,多個進程同時操作一個文件。
那如果我們想要多線程上傳怎么辦?改代碼吧(主要是后臺邏輯)。
前端代碼:
//并發上傳(多線程上傳)
var uploader=WebUploader.create({
//兼容老版本IE
swf: '/Scripts/webuploader-0.1.5/Uploader.swf',
// 文件接收服務端。
server: '/Webuploader/SveFile3',
// 開起分片上傳。
chunked: true,
//分片大小
chunkSize: 1000000,
//上傳并發數
threads: 10,
// 選擇文件的按鈕。
pick: '#picker'
});
// 點擊觸發上傳
$("#ctlBtn").click(function () {
uploader.upload();
});
uploader.on('uploadSuccess', function (file) {
//上傳完成后,給后臺發送一個合并文件的命令
$.ajax({
url: "/Webuploader/FileMerge",
data: { "fileName": file.name },
type: "post",
success: function () {
alert("上傳成功");
}
});
});
后臺代碼:
public string SveFile3()
{
var chunk=Request.Form["chunk"];//當前是第多少片
var path=Server.MapPath("~/App_Data/") + Path.GetFileNameWithoutExtension(Request.Files
if (!Directory.Exists(path))//判斷是否存在此路徑,如果不存在則創建
{
Directory.CreateDirectory(path);
}
//保存文件到根目錄 App_Data + 獲取文件名稱和格式
var filePath=path + "/" + chunk;
//創建一個追加(FileMode.Append)方式的文件流
using (FileStream fs=new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
using (BinaryWriter bw=new BinaryWriter(fs))
{
//讀取文件流
BinaryReader br=new BinaryReader(Request.Files[0].InputStream);
//將文件留轉成字節數組
byte[] bytes=br.ReadBytes((int)Request.Files[0].InputStream.Length);
//將字節數組追加到文件
bw.Write(bytes);
}
}
return "保存成功";
}
/// <summary>
/// 合并文件
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public bool FileMerge()
{
var fileName=Request.Form["fileName"];
var path=Server.MapPath("~/App_Data/") + Path.GetFileNameWithoutExtension(fileName);
//這里排序一定要正確,轉成數字后排序(字符串會按1 10 11排序,默認10比2小)
foreach (var filePath in Directory.GetFiles(path).OrderBy(t=> int.Parse(Path.GetFileNameWithoutExtension(t))))
{
using (FileStream fs=new FileStream(Server.MapPath("~/App_Data/") + fileName, FileMode.Append, FileAccess.Write))
{
byte[] bytes=System.IO.File.ReadAllBytes(filePath);//讀取文件到字節數組
fs.Write(bytes, 0, bytes.Length);//寫入文件
}
System.IO.File.Delete(filePath);
}
Directory.Delete(path);
return true;
}
到這里你以為就結束了嗎?錯,還有好多情況沒有考慮到。如果多個用戶上傳的文件名字一樣會怎樣?如何實現斷點續傳?還沒實現選擇多個文件?不過,這里不打算繼續貼代碼了(再貼下去,代碼量越來越多了),自己也來練習練習吧。
提供一個思路,上傳前先往數據庫插入一條數據。數據包含文件要存的路徑、文件名(用GUID命名,防止同名文件沖突)、文件MD5(用來識別下次續傳和秒傳)、臨時文件塊存放路徑、文件是否完整上傳成功等信息。
然后如果我們斷網后再傳,首先獲取文件MD5值,看數據庫里面有沒上傳完成的文件,如果有就實現秒傳。如果沒有,看是不是有上傳了部分的。如果有接著傳,如果沒有則重新傳一個新的文件。
之前我一直很疑惑,為什么上傳文件一定要用form包起來,現在算是大概明白了。
最開始在javascript還不流行時,我們就可以直接使用submit按鈕提交表單數據了。表單里面可以包含文字和文件。然后隨著js和ajax的流行,可以利用ajax直接異步提交部分表單數據。這里開始我就糾結了,為什么ajax可以提交自己組裝的數據。那為什么不能直接提交文件呢。這里我錯了,ajax提交的并不是隨意的數據,最后還是組裝成了表單格式(因為后臺技術對表單格式數據的支持比較普及)。但是現有的技術還不能通過js組裝一個文件格式的表單數據。直到H5中的FormData出現,讓前端js組裝一個包含文件的表單格式數據成為了可能。所以說表單只是為了滿足和后臺“約定”的數據格式而已。
相關推薦
demo
cikit-learn 的 datasets 模塊包含測試數據相關函數,主要包括三類:
data:特征數據數組,是 n_samples * n_features的二維 numpy.ndarray 數組
target:標簽數組,是 n_samples 的一維 numpy.ndarray 數組
DESCR:數據描述
feature_names:特征名
target_names:標簽名
提供 svmlight / libsvm 格式數據的導入或導出。
UCI Machine Learning Repository:http://archive.ics.uci.edu/ml/datasets.html
UCI KDD:http://kdd.ics.uci.edu/summary.data.type.html
Kaggle:https://www.kaggle.com/datasets
官方datasets包文檔:http://scikit-learn.org/stable/datasets/index.html
API列表:http://scikit-learn.org/stable/modules/classes.html#module-sklearn.datasets
*請認真填寫需求信息,我們會在24小時內與您取得聯系。