mvc+layui做图⽚上传(⼆)——使⽤流上传和下载图⽚
深入理解mvc摘要:上篇⽂章写到⼀种上传图⽚的⽅法,其中提到那种⽅法的局限性,就是上传的⽂件只能保存在本项⽬⽬录下,在其他⽬录中访问不到该⽂件。这与浏览器的安全性机制有关,浏览器不允许⽤户⽤任意的路径访问服务器上的资源,因为这可能造成服务器上其他位置的信息被泄露。浏览器只允许⽤户⽤相对路径直接访问本项⽬路径下的资源。那么,如果A项⽬要访问B项⽬上传的⽂件资源,这就产⽣问题了。所以这就需要另外⼀种⽅法来解决这个问题,那就是通过流(Stream)的形式上传和下载⽂件资源。这种⽅法因为不是通过路径直接访问⽂件,⽽是先把⽂件读取的流中,然后将流中的数据写⼊到新的⽂件中,还原需要上传的⽂件,所以也就不存在上⾯的问题了。本⽚博客,着重介绍⼀下这种⽅式的实现。
⼀、准备⼯作
⾸先,还是做⼀下准备⼯作:
(1)创建⼀个解决⽅案(图⽚上传),⼀个mvc项⽬(Console);
(2)然后新建控制器(UploadImageController.cs);
如图:
我这个demo是在⼀个code first实现案例上写的,所以你看到这个解决⽅案还有其他⼏个项⽬在⾥⾯,但是不⽤担⼼,本案例只涉及mvc项⽬(Console),不与其他⼏个项⽬产⽣依赖。
(3)引⼊layui相关的依赖,编写前端代码:
本案例中前台页⾯使⽤的是layui,所以提前引⼊layui的依赖,然后写好页⾯的代码(该代码⾃layui⽹站上copy),如下:
html:
<link href="~/Content/layui/css/layui.css" rel="stylesheet" />
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Content/layui/layui.js"></script>
<script src="~/Content/layui/layui.all.js"></script>
<div class="layui-upload" >
<button type="button" class="layui-btn" id="test1">上传图⽚</button>
<div class="layui-upload-list">
<img class="layui-upload-img" id="demo1" >
<p id="demoText"></p>
</div>
</div>
js:
<script type="text/javascript">
layui.use('upload', function(){
var $ = layui.jquery, upload = layui.upload;
c语言生成1 100随机数代码//普通图⽚上传
var uploadInst = der({
elem: '#test1',
url: '@Url.Action("Upload", "UploadImage")'
,
before: function(obj){
//预读本地⽂件⽰例,不⽀持ie8
obj.preview(function(index, file, result){
$('#demo1').attr('src', result); //图⽚链接(base64)
});
}
,done: function(res){
//如果上传失败
alert(JSON.stringify(res));
// return layer.msg("上传成功");
//上传成功
}
,error: function(){
//演⽰失败状态,并实现重传
var demoText = $('#demoText');
demoText.html('<span >上传失败</span> <a class="layui-btn layui-btn-xs demo-reload">重试</a>');
demoText.find('.demo-reload').on('click', function(){
uploadInst.upload();
});
}
});
});
</script>
以上代码为layui的图⽚上传⽰例代码,可到layui ⽂件上传部分获取。
上⾯的代码中,只需把url处的链接换成后台的图⽚上传⽅法即可。
如图所⽰:
就⼀个按钮,上⾯和下⾯的内容都是母版页⾥⾃带的。
⼆、上传功能实现
1.简述流上传⽂件的过程
在使⽤流上传⽂件时,最好通过阅读书籍,对相关的知识有⼀定的了解。使⽤流上传⽂件与直接上传⽂件相⽐,过程更复杂,这其实相当于把⼀个⽂件由整拆为零,传输到对应位置后再由零重建为整的⼀个过程。
关于流的使⽤中,有⼏个点需要了解:
(1)路径:path,这是⽂件会被保存的地⽅,通常会使⽤ Path.Conbine(path1,path2). 将路径和⽂件名组合为⼀个完整的路径,如下:
string filePath = Path.Combine(@"D:\Asp.Net\C#code\C#基础补习\Upload",fileName);
(2)缓存数组:buffer,这是⼀个字节类型的数组,输⼊流中的数据会被依次存储到缓存数组中,然后缓存数组把其中的数据写到新的流
(输出流)中;
byte[] buffer;
(3)FileStream:⽂件流,这个类主要⽤于在⼆进制⽂件中 “读” 和 “写” ⼆进制数据。上图中流读取⽂件和写⼊⽂件都是过这个类来实现的。
下⾯给出⼏条⽰例:
var inputStream = new FileStream(inputfile,FileMode.Open,FileAccess.Read,FileShare.Read);
上⼀句创建⼀个⽂件流的对象,这个对象有⼏个参数,⽤于控制这个流来进⾏什么样的操作:
个人主页网页设计源代码inputfile:这是⼀个⽂件路径,表⽰把这个路径指定的⼆进制⽂件读⼊到流中。如:
var inputStream = new FileStream(@“D:\Asp.Net\C#code\C#基础补习\Upload\1.jpg”,FileMode.Open,FileAccess.Read);
就是把这个1.jpg读⼊到流中。
FileMode:指定系统打开选定的⽂件的⽅式,有以下⼏个选项(枚举值):
//
// 摘要:
// 指定操作系统打开⽂件的⽅式。
[ComVisible(true)]
public enum FileMode
{
//
// 摘要:
// 指定操作系统应创建⼀个新的⽂件。这要求 System.Security.Permissions.FileIOPermissionAccess.Write
java中正则表达式用法// 权限。如果该⽂件已存在, System.IO.IOException 则会引发异常。
CreateNew = 1,
//
// 摘要:
// 指定操作系统应创建⼀个新的⽂件。如果该⽂件已存在,则会覆盖它。这要求 System.Security.Permissions.FileIOPermissionAccess.W // 权限。 FileMode.Create 等效于请求,如果该⽂件不存在,则使⽤ System.IO.FileMode.CreateNew; 否则为使⽤ System.IO.FileMode.Tru // 如果该⽂件已存在但为隐藏的⽂件, System.UnauthorizedAccessException 则会引发异常。
Create = 2,
//
// 摘要:
// 指定操作系统应打开现有⽂件。若要打开该⽂件的能⼒是依赖于指定的值 System.IO.FileAccess 枚举。⼀个 System.IO.FileNotFoundEx // 如果⽂件不存在将引发异常。
Open = 3,
//
// 摘要:
// 指定操作系统应打开⼀个⽂件,是否它存在,则否则,应创建⼀个新的⽂件。如果使⽤打开该⽂件 FileAccess.Read, ,System.Security
// 权限是必需的。如果⽂件访问是 FileAccess.Write, ,System.Security.Permissions.FileIOPermissionAccess.Write
// 权限是必需的。如果使⽤打开该⽂件 FileAccess.ReadWrite, ,这两个 System.Security.Permissions.FileIOPermissionAccess.Read
// 和 System.Security.Permissions.FileIOPermissionAccess.Write 权限是必需的。
jquery下载文件请求OpenOrCreate = 4,
//
// 摘要:
// 指定操作系统应打开现有⽂件。当打开⽂件时,应被截断,以便其⼤⼩为零字节。这要求 System.Security.Permissions.FileIOPermissio // 权限。尝试从⽂件中读取使⽤打开 FileMode.Truncate 导致 System.ArgumentException 异常。
Truncate = 5,
//
// 摘要:
// 如果它存在,并且查到该⽂件的末尾,或者创建⼀个新⽂件,请打开该⽂件。这要求 System.Security.Permissions.FileIOPermissionA // 权限。 FileMode.Append 可以仅在结合使⽤ FileAccess.Write。尝试查该⽂件将引发结束之前将其置于 System.IO.IOException
// 异常,并且任何尝试读取失败,将引发 System.NotSupportedException 异常。
Append = 6
}
常⽤的⼏个项为:FileMode.Create /CreateNew/Open/OpenOrCreate,
其中Open表⽰这个流会打开这个⽂件,Create表⽰会在该路径下创建⼀个这个命名的⽂件,
FileMode和FileAccess共同控制流对⽂件进⾏操作的⽅式。
FileAccess:控制对该⽂件进⾏读或者写的权限,⽐如,你要上传⼀个⽂件,那么你⾸先要读取这个⽂件⾥的数据,那这个就要设置为读,
⼜⽐如,某个⽂件的数据已经读到缓存区了,需要把它存到指定的位置,那么这个时候,就要把数据写⼊⼀个新的⽂件,那么就要⽤写。这
个也有⼏个选项(枚举值):
// 摘要:
// 对于读、写或读/写访问的⽂件中定义的常数。
[ComVisible(true)]
[Flags]
public enum FileAccess
{
//
// 摘要:
// 对⽂件的读取访问权限。可以从⽂件读取数据。将与结合起来 Write 为读/写访问。
Read = 1,
//
// 摘要:
// 对⽂件的写⼊访问权限。数据可以写⼊该⽂件。将与结合起来 Read 为读/写访问。
Write = 2,springboot如何整合组件
//
// 摘要:
// 读取和写⼊到⽂件的访问。可以写⼊和从⽂件中读取数据。
ReadWrite = 3
}
FileMode和FileAccess对应起来使⽤,⼀般Open和Read组合,Create和Write组合。
(4)偏移量 offset:流中的数据写⼊(或读出)到缓存数组中时,数据是按照类似排队的顺序,⼀个⼀个写的,流中有⼀个指针⼀样的东
西,数据读了⼏个,这个指针就向前移动⼏位,指针移动的多少就是偏移量,偏移量作为流的使⽤中的⼀个重要的参数,在⽂件分段上传中
作⽤明显。
2.上传功能的实现:
这⾥我直接给出代码,代码⾥有详细的解释,不再另作说明:
public string Upload()
{
///获取上传的⽂件
var file = Request.Files[0];
//获取上传⽂件的⽂件名
string fileName = file.FileName;
//上传路径
string filePath = Path.Combine(@"D:\Asp.Net\C#code\C#基础补习\Upload",fileName);
//定义缓存数组
byte[] buffer;
//将⽂件数据塞到流⾥
var inputStream = file.InputStream;
///获取读取数据的长度
int readLength = Convert.ToInt32(inputStream.Length);
/
//给缓存数组指定⼤⼩
buffer = new byte[readLength];
//设置指针的位置为最开始的位置
inputStream.Seek(0,SeekOrigin.Begin);
//从位置 0 开始读取上传的⽂件的数据,数据读取到第⼀个参数buffer(缓存区)中
inputStream.Read(buffer,0,readLength);
//创建输出⽂件流,指定⽂件的输出位置,模式为创建该新⽂件,读写权限为写
using (var outputStream = new FileStream(filePath,FileMode.Create,FileAccess.Write)) {
//设置指针的位置为最开始的位置
outputStream.Seek(0,SeekOrigin.Begin);
//从起始位置将第⼀个参数 buffer(缓存区)⾥的数据写⼊到 filePath 指定的⽂件中 outputStream.Write(buffer,0,buffer.Length);
}
//向前台返回上传⽂件的⽂件名,表⽰上传成功
return JsonConvert.SerializeObject(new { Name = fileName });
}
写好该⽂件后,将前端js中的 url 处写上指向该代码的链接,然后运⾏,查看结果:如图所⽰:
然后,打开对应⽬录的⽂件夹,查看⽂件是否已上传:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论