.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

时间:2022-09-21 10:26:44

摘要:上篇文章写到一种上传图片的方法,其中提到那种方法的局限性,就是上传的文件只能保存在本项目目录下,在其他目录中访问不到该文件。这与浏览器的安全性机制有关,浏览器不允许用户用任意的路径访问服务器上的资源,因为这可能造成服务器上其他位置的信息被泄露。浏览器只允许用户用相对路径直接访问本项目路径下的资源。那么,如果A项目要访问B项目上传的文件资源,这就产生问题了。所以这就需要另外一种方法来解决这个问题,那就是通过 流(Stream)的形式上传和下载文件资源。这种方法因为不是通过路径直接访问文件,而是先把文件读取的流中,然后将流中的数据写入到新的文件中,还原需要上传的文件,所以也就不存在上面的问题了。本片博客,着重介绍一下这种方式的实现。

一、准备工作

首先,还是做一下准备工作:

(1)创建一个解决方案(图片上传),一个mvc项目(Console);

(2)然后新建控制器(UploadImageController.cs);

如图:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

我这个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" style="margin-top:100px;">
<button type="button" class="layui-btn" id="test1">上传图片</button>
<div class="layui-upload-list">
<img class="layui-upload-img" id="demo1" style="width:100px;height:auto;">
<p id="demoText"></p>
</div>
</div>

js:

<script type="text/javascript">
layui.use('upload', function(){
var $ = layui.jquery, upload = layui.upload;
//普通图片上传
var uploadInst = upload.render({
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 style="color: #FF5722;">上传失败</span> <a class="layui-btn layui-btn-xs demo-reload">重试</a>');
demoText.find('.demo-reload').on('click', function(){
uploadInst.upload();
});
}
});
});
</script>

以上代码为layui的图片上传示例代码,可到layui 文件上传部分获取。

上面的代码中,只需把url处的链接换成后台的图片上传方法即可。

如图所示:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

就一个按钮,上面和下面的内容都是母版页里自带的。

二、上传功能实现

 1.简述流上传文件的过程

在使用流上传文件时,最好通过阅读书籍,对相关的知识有一定的了解。使用流上传文件与直接上传文件相比,过程更复杂,这其实相当于把一个文件 由整拆为零,传输到对应位置后再 由零重建为整 的一个过程。

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

关于流的使用中,有几个点需要了解:

(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
// 权限。 如果该文件已存在, System.IO.IOException 则会引发异常。
CreateNew = ,
//
// 摘要:
// 指定操作系统应创建一个新的文件。 如果该文件已存在,则会覆盖它。 这要求 System.Security.Permissions.FileIOPermissionAccess.Write
// 权限。 FileMode.Create 等效于请求,如果该文件不存在,则使用 System.IO.FileMode.CreateNew; 否则为使用 System.IO.FileMode.Truncate。
// 如果该文件已存在但为隐藏的文件, System.UnauthorizedAccessException 则会引发异常。
Create = ,
//
// 摘要:
// 指定操作系统应打开现有文件。 若要打开该文件的能力是依赖于指定的值 System.IO.FileAccess 枚举。 一个 System.IO.FileNotFoundException
// 如果文件不存在将引发异常。
Open = ,
//
// 摘要:
// 指定操作系统应打开一个文件,是否它存在,则否则,应创建一个新的文件。 如果使用打开该文件 FileAccess.Read, ,System.Security.Permissions.FileIOPermissionAccess.Read
// 权限是必需的。 如果文件访问是 FileAccess.Write, ,System.Security.Permissions.FileIOPermissionAccess.Write
// 权限是必需的。 如果使用打开该文件 FileAccess.ReadWrite, ,这两个 System.Security.Permissions.FileIOPermissionAccess.Read
// 和 System.Security.Permissions.FileIOPermissionAccess.Write 权限是必需的。
OpenOrCreate = ,
//
// 摘要:
// 指定操作系统应打开现有文件。 当打开文件时,应被截断,以便其大小为零字节。 这要求 System.Security.Permissions.FileIOPermissionAccess.Write
// 权限。 尝试从文件中读取使用打开 FileMode.Truncate 导致 System.ArgumentException 异常。
Truncate = ,
//
// 摘要:
// 如果它存在,并且查找到该文件的末尾,或者创建一个新文件,请打开该文件。 这要求 System.Security.Permissions.FileIOPermissionAccess.Append
// 权限。 FileMode.Append 可以仅在结合使用 FileAccess.Write。 尝试查找该文件将引发结束之前将其置于 System.IO.IOException
// 异常,并且任何尝试读取失败,将引发 System.NotSupportedException 异常。
Append =
}

常用的几个项为:FileMode.Create /CreateNew/Open/OpenOrCreate,

其中Open表示这个流会打开这个文件,Create表示会在该路径下创建一个这个命名的文件,

FileMode和FileAccess共同控制流对文件进行操作的方式。

FileAccess:控制对该文件进行读或者写的权限,比如,你要上传一个文件,那么你首先要读取这个文件里的数据,那这个就要设置为 读 ,又比如,某个文件的数据已经读到缓存区了,需要把它存到指定的位置,那么这个时候,就要把数据写入一个新的文件,那么就要用写。这个也有几个选项(枚举值):

   // 摘要:
// 对于读、 写或读/写访问的文件中定义的常数。
[ComVisible(true)]
[Flags]
public enum FileAccess
{
//
// 摘要:
// 对文件的读取访问权限。 可以从文件读取数据。 将与结合起来 Write 为读/写访问。
Read = ,
//
// 摘要:
// 对文件的写入访问权限。 数据可以写入该文件。 将与结合起来 Read 为读/写访问。
Write = ,
//
// 摘要:
// 读取和写入到文件的访问。 可以写入和从文件中读取数据。
ReadWrite =
}

FileMode和FileAccess对应起来使用,一般Open和Read组合,Create和Write组合。

(4)偏移量 offset:流中的数据写入(或读出)到缓存数组中时,数据是按照类似排队的顺序,一个一个写的,流中有一个指针一样的东西,数据读了几个,这个指针就向前移动几位,指针移动的多少就是偏移量,偏移量作为流的使用中的一个重要的参数,在文件分段上传中作用明显。

2.上传功能的实现:

这里我直接给出代码,代码里有详细的解释,不再另作说明:

        public string Upload()
{
///获取上传的文件
var file = Request.Files[];
//获取上传文件的文件名
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(,SeekOrigin.Begin);
//从位置 0 开始读取上传的文件的数据,数据读取到第一个参数buffer(缓存区)中
inputStream.Read(buffer,,readLength);
//创建输出文件流,指定文件的输出位置,模式为创建该新文件,读写权限为 写
using (var outputStream = new FileStream(filePath,FileMode.Create,FileAccess.Write))
{
//设置指针的位置为 最开始 的位置
outputStream.Seek(,SeekOrigin.Begin);
//从起始位置 将 第一个参数 buffer(缓存区)里的数据写入到 filePath 指定的文件中
outputStream.Write(buffer,,buffer.Length);
}
        //向前台返回上传文件的文件名,表示上传成功
return JsonConvert.SerializeObject(new { Name = fileName });
}

写好该文件后,将前端js中的 url 处写上指向该代码的链接, 然后运行,查看结果:如图所示:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

然后,打开对应目录的文件夹,查看文件是否已上传:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

 3.另一种写法,针对比较大的文件

上一种方法我们给定数组的大小是根据流的长度来确定的,因为这里是上传的图片,数据量不是很大,这样做没什么问题,但是上传的文件比较大的话,文件可能不会很顺利的上传。

这里提供另外一种上传方法,当然,还是用 流 上传 ,但不是定义一个 刚刚好的数组 ,一次性上传,而是定义一个固定大小的数组,每次取一定量的数据,然后把数据写到新文件中,再清空数组,之后又用数组去取定量的数据,再写入,在取数据,再写入,像这样循环往复,直至文件上传完毕为止。下面是这种方法的代码,同样有比较详细的注释,不再另作说明:

        /// <summary>
/// 文件上传
/// </summary>
/// <returns></returns>
public string UploadFile()
{
var file = Request.Files[];
int BUFFERSIZE = ;
var name = string.Empty;
// var uploadTime = DateTime.Now;
///文件上传的最底层目录路径 格式为 \文件id\文件名
var fileId = Guid.NewGuid();
name = file.FileName;
// var uploadPath = Path.Combine(fileId.ToString(), name);
///文件上传后的位置
var outputPath = Path.Combine(@"D:\Asp.Net\C#code\C#基础补习\Upload", name);
///将接收到的文件转化为流
var inputStream = file.InputStream;
///流数据读取到数组中的偏移量
long offset = ;
///获取或设置光标在当前流中的位置
inputStream.Position = offset;
///存储流中数据的数组
// byte[] buffer = new byte[BUFFERSIZE];
///读取流中的数据,读到数组中
while (offset < inputStream.Length)
{
///存储流中数据的数组,该数组大小根据流中未读取数据量大小调整,若未读取数据量大于规定的数组最大大小,则数组大小设为该数组的最大容量
byte[] buffer = new byte[Math.Min(BUFFERSIZE,inputStream.Length - offset)];
int nRead = inputStream.Read(buffer,,buffer.Length);
if (nRead < )
{
///若读取完毕,则跳出循环
break;
}
try
{
///将读取到数组中的数据写入新的文件中,再保存到指定的位置
using (var outputStream = new FileStream(outputPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
outputStream.Seek(offset, SeekOrigin.Begin);//将流中的光标移到第一次读取的数据之后
outputStream.Write(buffer, , buffer.Length);
outputStream.Flush(); offset = outputStream.Length;
}
}
catch (Exception exception)
{ throw exception;
} }
return JsonConvert.SerializeObject(new { Id = fileId,Name = name});
}

同样,演示一下这种方法是否能成功:

先把url处改为 @Url.Action("UploadFile","UploadImage"),

,.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

效果如下:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

三、下载文件

既然有文件上传,按必然就少不了文件下载,下面给出一个文件下载的功能实现。

首先,在前端页面添加一个 a标签按钮 和 一个图片链接 按钮,如下图所示:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

<div>
<a href="@Url.Action("DownloadFile","UploadImage")">下载图片</a>
<img src="@Url.Action("DownloadFile","UploadImage")" alt="Alternate Text" style="width:100px;height:auto;"/>
</div>

其中DownloadFile是后台代码,然后给出后台代码,由于下载是上传的逆过程,所以这里不再做出详细解释:

       /// <summary>
/// 文件下载 ,该案例仅为一个文件下载的demo,其文件名和路径等信息,此处直接给出固定值,实际应用中可根据需求灵活给定文件名和路径
/// </summary>
/// <returns>返回文件</returns>
public ActionResult DownloadFile()
{
string fileName = "角楼.jpg";
byte[] buffer;
string contentType = "application/octec-stream";
///MemoryStream()内存流
using (var outputStream = new MemoryStream())
{
try
{ long offset = ;
string inputFilePath = Path.Combine(@"D:\Asp.Net\C#code\C#基础补习\Upload", fileName);
using (var inputStream = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read))
{
long readLength = inputStream.Length;
buffer = new byte[readLength];
inputStream.Seek(offset,SeekOrigin.Begin);
inputStream.Read(buffer,,Convert.ToInt32(readLength));
}
}
catch (Exception exception)
{ throw exception;
}
outputStream.Write(buffer,,buffer.Length);
return File(outputStream.GetBuffer(),contentType,fileName); }
}

下面给出演示图片:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

下载此图:

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片

文件默认下载到电脑上的  “下载” ,文件夹。

关于文件.net mvc下另一种图片上传的方法就介绍到这里,本篇只着重介绍文件上传和下载的过程,实际应用中会有很多其他方面的点要涉及,这里不进行说明,如果时间允许,会再介绍。

本程序的源代码需要的同学可给留言 邮箱 ,我会第一时间发给你。

本人系5个月.net程序员 ,菜鸡一只,以上所述,如有重大谬误,大牛请狠批!

我的联系方式:eMail:3074596466@qq.com

祝大家小年快乐!

如有帮助,能不能点个推荐呢,哈哈哈!有点恬不知耻哈,不要介意!

.net mvc + layui做图片上传(二)—— 使用流上传和下载图片的更多相关文章

  1. 文件上传二:FormData上传

    介绍三种上传方式: 文件上传一:伪刷新上传 文件上传二:FormData上传 文件上传三:base64编码上传 Flash的方式也玩过,现在不推荐用了. 真正的异步上传,FormData的更多操作,请 ...

  2. &period;net mvc &plus; layui做图片上传(一)

    图片上传和展示是互联网应用中比较常见的一个功能,最近做的一个门户网站项目就有多个需要上传图片的功能模块.关于这部分内容,本来功能不复杂,但后面做起来却还是出现了一些波折.因为缺乏经验,对几种图片上传的 ...

  3. cropper&period;js 二次开发:截图并下载图片

    cropper.js 是一个基于jquery的图片截取库. 参考:https://blog.csdn.net/weixin_38023551/article/details/78792400 我的代码 ...

  4. ASP&period;NET MVC&plus;LayUI视频上传

    前言: 前段时间在使用APS.NET MVC+LayUI做视频上传功能的时,发现当上传一些内存比较大的视频就会提示上传失败,后来通过查阅相关资料发现.NET MVC框架为考虑安全问题,在运行时对请求的 ...

  5. SpringBoot图片上传&lpar;四&rpar; 一个input上传N张图,支持各种类型

    简单介绍:需求上让实现,图片上传,并且可以一次上传9张图,图片格式还有要求,网上找了一个测试了下,好用,不过也得改,仅仅是实现了功能,其他不尽合理的地方,还需自己打磨. 代码: //html<d ...

  6. SpringMVC单文件上传、多文件上传、文件列表显示、文件下载(转)

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文详细讲解了SpringMVC实例单文件上传.多文件上传.文件列表显示.文件下载. 本文工程 ...

  7. &lbrack;转&rsqb;SpringMVC单文件上传、多文件上传、文件列表显示、文件下载

    一.新建一个Web工程,导入相关的包 springmvc的包+commons-fileupload.jar+connom-io.jar+commons-logging,jar+jstl.jar+sta ...

  8. php 合并图片 &lpar;将活动背景图片和动态二维码图片合成一张图片&rpar;

    <?php //案例一:将活动背景图片和动态二维码图片合成一张图片 //图片一 $path_1 = './background.png'; //图片二 $path_2 = './FU0851_2 ...

  9. Day3-scrapy爬虫下载图片自定义名称

    学习Scrapy过程中发现用Scrapy下载图片时,总是以他们的URL的SHA1 hash值为文件名,如: 图片URL:http://www.example.com/image.jpg 它的SHA1 ...

随机推荐

  1. Centos下编译JDK

    因为OpenJDK是开源的,这里使用openJDK进行编译联系 环境要求 Centos6.7 64位 openjdk-7u40-fcs-src-b43-26_aug_2013.zip bootstra ...

  2. Oracle Length 和 Lengthb 函数说明 &period;&lpar;用来判断记录值里是否有中文内容&rpar;

    一.官网的说明 http://download.oracle.com/docs/cd/E11882_01/server.112/e26088/functions088.htm#SQLRF00658 P ...

  3. MVC架构学习

    作为一名小小的GreenBird,学习MVC呢,已经花费了2天了,期间得到了美丽的学姐的帮助,初步整理了一下. 首先,学习MVC呢就先以一个标准的MVC的简单的例子来入手,然后根据学姐的PPT,我用v ...

  4. StringBuilder和string&period;Format性能对比

    本文由博主(YinaPan)原创,转载请注明出处:http://www.cnblogs.com/YinaPan/p/sbformat.html StringBuilder的性能优于string.For ...

  5. 命令行bash的基础操作

    刚进入系统在光标前面会显示这样一串字符[root@centeros ~]# root表示当前的登录用户可以通过id命令查看 centeros表当前的主机名可以通过hostname查看 ~表示当前用户的 ...

  6. Python CGI编程和CGIHTTPServer

    Python2.7 的CGIHTTPServer 可以作为一个简单的HTTP服务器,能够调用cgi脚本 1 在任意目录下创建一个特殊的目录 cgi-bin ,用于存放自己写的脚本(.py或.cgi) ...

  7. React开发的一些注意点

    react是R系技术栈中最基础同时也是最核心的一环,2年不到获取了62.5k star(截止到目前),足可见其给力程度.下面对一些react日常开发中的注意事项进行罗列.建议初学的朋友还是先过一遍这篇 ...

  8. Linux:Day17&lpar;上&rpar; gawk基础

    GNU awk: 文本处理三工具:grep,sed,awk grep,egrep,fgrep:文本过滤工具:pattern sed:行编辑器 模式空间.保持空间 awk:报告生成器,格式化文本输出: ...

  9. 等差数列 &lbrack;USACO Training Section 1&period;4&rsqb;

    题目描述 一个等差数列是一个能表示成a, a+b, a+2b,…, a+nb (n=0,1,2,3,…)的数列. 在这个问题中a是一个非负的整数,b是正整数.写一个程序来找出在双平方数集合(双平方数集 ...

  10. 20181023-3 每周例行报告(添加PSP)

    此作业要求:[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2100] 一.本周PSP表格 类型 任务 开始时间 结束时间 中断时间 净时 ...