WebApi 文件上传,断点上传,分块上传,断点下载,查询 (图片的直接预览,视频边加载边播放)

using Manjinba.Communication.Common.Caching;
using Manjinba.Communication.Common.Logging;
using Manjinba.Communication.Common.Utils;
using Manjinba.Communication.IRepository;
using Manjinba.Communication.IService;
using Manjinba.Communication.Model;
using Manjinba.Communication.Model.ConstVals;
using Manjinba.Communication.Model.Enums;
using Manjinba.Communication.Model.Response;
using Manjinba.Communication.Service.Caching;
using Manjinba.Communication.WebApiFtp.Attributes;
using Manjinba.Communication.WebApiFtp.Provider;
using Manjinba.Communication.WebApiFtp.Resources;
using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Results;

namespace Manjinba.Communication.WebApiFtp.Controllers.V1_5
{
/// <summary>
///
/// </summary>
//[RoutePrefix("api/v1.4/fileoperator")]
public class FileOperatorController : ApiController
{
/// <summary>
/// 文件仓储层
/// </summary>
[Dependency]
public IFileGuideRepository fileRepository { get; set; }
/// <summary>
/// 文件操作服务
/// </summary>
[Dependency]
public IFileOperatorService fileOperatorService { get; set; }

#region 配置文件参数
/// <summary>
/// 缓存配置
/// </summary>
private readonly string cacheTime = ConfigUtil.GetConfigStr("Cache-Control");
/// <summary>
/// 上传文件最大大小
/// </summary>
private readonly int maxSizeUploadFile = ConfigUtil.GetConfigInt("maxSizeUploadFile") == 0 ? 104857600 : ConfigUtil.GetConfigInt("maxSizeUploadFile");
/// <summary>
/// ftp服务节点名
/// </summary>
private readonly string ftpServerNodeName = ConfigUtil.GetConfigStr("FtpServerNodeName");
/// <summary>
/// 文件MD5缓存过期小时数
/// </summary>
private readonly string ftpFileCacheExpireHour = ConfigUtil.GetConfigStr("FtpFileCacheExpireHour");
/// <summary>
/// 聊天文件失效时间(小时)
/// </summary>
private readonly string chatFileExpireHour = ConfigUtil.GetConfigStr("ChatFileExpireHour");
/// <summary>
/// 分块文件失效时间(小时)
/// </summary>
private readonly string blockFileExpireHour = ConfigUtil.GetConfigStr("BlockFileExpireHour");
/// <summary>
/// 断点续传文件失效时间(小时)
/// </summary>
private readonly string breakResumeFileExpireHour = ConfigUtil.GetConfigStr("BreakResumeFileExpireHour");
/// <summary>
/// 日期格式路径
/// </summary>
private readonly string dateTimePath = DateTime.Now.Year.ToString().PadLeft(2, ‘0‘)
+ "/" + DateTime.Now.Month.ToString().PadLeft(2, ‘0‘)
+ "/" + DateTime.Now.Day.ToString().PadLeft(2, ‘0‘);
/// <summary>
/// 合并后是否删除分块文件
/// </summary>
private readonly bool? isCombineDelete = ConfigUtil.GetConfigStr("IsCombineDelete")?.ToBool();
/// <summary>
/// FTP根路径
/// </summary>
private readonly string ftprootpath = ConfigUtil.GetConfigStr("RootFtpPath").TrimStart(‘/‘).TrimEnd(‘/‘);
#endregion

#region 文件上传
/// <summary>
/// 多文件上传(流方式)上传文件根据上传文件类型进行区分
/// </summary>
/// <returns></returns>
[IgnoreRequestStreamLog]
//[HttpPost, Route("UploadFile")]
[HttpPost]
public async Task<JsonResult<UploadResponse>> UploadFile()
{
// Check whether the POST operation is MultiPart?
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var response = new UploadResponse
{
uploadResult = new List<FileUploadResult>(),
// 文件保存失败
code = (int)FileOperatorStatusCode.SaveFileDefeated,
error = FileOperatorStatusCode.SaveFileDefeated.GetEnumText()
};
#region 取流文件,然后决定是否保存文件至Ftp
var provider = new MultipartFormDataMemoryStreamProvider();
// Read the form and file data.
UploadResponse uploadResponse = await Request.Content.ReadAsMultipartAsync(provider)
.ContinueWith(o =>
{
try
{
var isRangeRequest = false;
long? startPosition = null;
long? endPosition = null;
var rangeFileCode = string.Empty;
if (Request.Headers.Range != null && Request.Headers.Range.Ranges.Count > 0)
{
isRangeRequest = true;
RangeItemHeaderValue range = Request.Headers.Range.Ranges.First();
startPosition = range.From.HasValue ? range.From : null;
endPosition = range.To.HasValue ? range.To : null;
}
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
var fileUploadResult = new FileUploadResult();
//接收文件及输出上传文件类型
var oldFileName = file.Headers.ContentDisposition.FileName.Trim(‘"‘); // 获取上传文件实际的文件名
var fileExt = oldFileName.Substring(oldFileName.LastIndexOf(‘.‘) + 1)?.ToLower();
int businessType = 0;//上传文件业务类型
if (!provider.FormData.AllKeys.Any(a => a.Equals("businessType", StringComparison.CurrentCultureIgnoreCase)))
{
response.code = -(int)FileOperatorStatusCode.ParameterError;
response.error = "请求参数错误,请求参数不包含业务类型信息!";
return response;
}
else
{
var businessTypeKey = provider.FormData.GetValues(
provider.FormData.AllKeys.Where(w => w.Equals("businessType", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()
).FirstOrDefault();
Int32.TryParse(businessTypeKey, out businessType);//从表单中获取到上传文件业务类型,输出赋值给busnissType
}
fileUploadResult.fileName = oldFileName;
#region 检查扩展名黑白名单
if (string.IsNullOrWhiteSpace(fileExt) || !FileExtensionUtil.IsWhiteContains(fileExt) || FileExtensionUtil.IsBlackContains(fileExt))
{
fileOperatorService.SetErrorResponse(response, fileUploadResult, FileOperatorStatusCode.NotSupportType);//不支持文件上传类型
continue;
}
#endregion
if (isRangeRequest)
{
if (!provider.FormData.AllKeys.Any(a => a.Equals("fileCode", StringComparison.CurrentCultureIgnoreCase)))
{
response.code = -(int)FileOperatorStatusCode.ParameterError;
response.error = "请求参数错误,不包含断点续传文件唯一信息";
return response;
}
else
{
rangeFileCode = provider.FormData.GetValues(
provider.FormData.AllKeys.Where(w => w.Equals("fileCode", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()
).FirstOrDefault();
if (rangeFileCode.Contains("."))
{
rangeFileCode = rangeFileCode.Substring(0, rangeFileCode.LastIndexOf(‘.‘));
}
}
}
using (var stream = ((MultipartFileDataStream)file).Stream)
{
#region 检查文件大小
var fileLength = (long)0;
try
{
fileLength = stream.Length;
}
catch (ObjectDisposedException ode)
{
LogHelper.GetLog().Error(ode.Message + ode.StackTrace);
response.code = -(int)FileOperatorStatusCode.ObjectDisposed;
response.error = "请求参数错误,请求参数不包含业务类型信息!";
return response;
}
// 最大文件大小
if (fileLength <= 0)
{
fileOperatorService.SetErrorResponse(response, fileUploadResult, FileOperatorStatusCode.NotExist);
continue;
}
else if (fileLength > maxSizeUploadFile)
{
fileOperatorService.SetErrorResponse(response, fileUploadResult, FileOperatorStatusCode.OverSize);
continue;
}
#endregion

#region 根据文件内容MD5查询文件是否已存在
var fileContentMD5 = isRangeRequest ? rangeFileCode : FileMD5HashCodeUtil.GetFileContentMD5HashCode(stream);//获取文件流生成MD5文件
var fileMd5Name = fileContentMD5 + "." + fileExt;
fileUploadResult.fileCode = fileMd5Name;
fileUploadResult.fileType = fileExt;
fileUploadResult.fileBusinessType = businessType;
var oldFilePath = string.Empty;
var fileMd5IsExist = fileOperatorService.CheckFileIsExist(fileMd5Name, businessType, out oldFilePath);
if (fileMd5IsExist)
{
fileOperatorService.SetSuccessResponse(response, fileUploadResult, FileOperatorStatusCode.AlreadyExists);
continue;
}
// 断点续传
if (isRangeRequest)
{
var breakFilePath = string.Empty;
var breakFileSize = (long)0;
var breakResumeMd5IsExist = fileOperatorService.CheckBreakResumeFileIsExist(fileMd5Name, businessType, out breakFilePath);
if (breakResumeMd5IsExist && !string.IsNullOrWhiteSpace(breakFilePath))
{
breakFileSize = FtpOperationUtil.GetFileSize(ExchangeFilePath(breakFilePath) + "/" + fileMd5Name);
}
if (startPosition != breakFileSize)
{
response.code = -(int)FileOperatorStatusCode.StartPositionError;
response.error = FileOperatorStatusCode.StartPositionError.GetEnumText();
return response;
}
}
#endregion

#region Ftp操作 上传Ftp
if (!fileMd5IsExist)
{
#region 上传Ftp服务器
// 获取完整存储路径(不含 ftp:// serverip)
var filePath = fileOperatorService.SaveFileToFtp(stream, fileMd5Name, FtpOperationUtil.GetFileDefaultPath(((BusinessFileType)businessType).ToString()));//数据流保存到FTP
stream.Position = 0;
if (string.IsNullOrEmpty(filePath))
{
LogHelper.GetLog(this).Error("返回路径为空");
fileOperatorService.SetErrorResponse(response, fileUploadResult, FileOperatorStatusCode.SaveFileDefeated);
continue;
}
// 判断文件是否完整
var isFileComplete = false;
if (isRangeRequest)
{
isFileComplete = FtpOperationUtil.CheckFileIsComplete(ExchangeFilePath(filePath) + "/" + fileMd5Name);
}
//var breakResumeFileSize = FtpOperationUtil.GetFileSize(ExchangeFilePath(filePath) + "/" + fileMd5Name);
#endregion

#region 不存在则存入内容MD5缓存
// Ftp文件信息
FtpFile ftpFile = new FtpFile();
ftpFile = new FtpFile
{
fileName = oldFileName,
businessType = businessType, //上传文件业务类型
nodeName = ftpServerNodeName,
filePath = filePath,
fileCode = fileMd5Name,
fileType = fileExt//,
//fileSize = breakResumeFileSize
};
var fileCodeKey = string.Empty;
var expireHour = string.Empty;
int fileExpireHour = 0;
// 不是断点续传 或 断点续传完成
if (!isRangeRequest || isFileComplete)
{
if ((BusinessFileType)businessType != BusinessFileType.ChatFile)
{
// 先写入数据库
var dbResult = fileOperatorService.AddFile(businessType, oldFileName, fileMd5Name, filePath);//添加文件信息
if (!dbResult)
{
LogHelper.GetLog(this).Error("写入数据库失败");
fileOperatorService.SetErrorResponse(response, fileUploadResult, FileOperatorStatusCode.DbSaveFileDefeated);
continue;
}
expireHour = chatFileExpireHour;
}
else
{
expireHour = ftpFileCacheExpireHour;
}
int.TryParse(expireHour, out fileExpireHour);
if (fileExpireHour == 0)
{
fileExpireHour = 24;
}
//fileCodeKey = (ConstVal.foreverFilePre + businessType + ":" + fileMd5Name).ToUpper();
fileCodeKey = (ConstVal.foreverFilePre + fileMd5Name).ToUpper();
fileOperatorService.AddToCache(fileCodeKey, ftpFile, fileExpireHour);
LogHelper.GetLog(this).DebugFormat("写入内容MD5缓存:filename:‘{0}‘, fileContentMD5:‘{1}‘", oldFileName, fileMd5Name);
fileOperatorService.SetSuccessResponse(response, fileUploadResult, FileOperatorStatusCode.SaveSuccess);
response.code = 0;
response.error = string.Empty;
// 如果是断点并上传完成,删除断点缓存
if (isRangeRequest && isFileComplete)
{
//var breakKey = (ConstVal.breakResumeFilePre + businessType + ":" + fileMd5Name).ToUpper();
var breakKey = (ConstVal.breakResumeFilePre + fileMd5Name).ToUpper();
using (var cache = new StackExchangeRedis())
{
cache.Remove(breakKey);
}
}
}
else
{
expireHour = breakResumeFileExpireHour;
int.TryParse(expireHour, out fileExpireHour);
if (fileExpireHour == 0)
{
fileExpireHour = 24;
}
//fileCodeKey = (ConstVal.breakResumeFilePre + businessType + ":" + fileMd5Name).ToUpper();
fileCodeKey = (ConstVal.breakResumeFilePre + fileMd5Name).ToUpper();
fileOperatorService.AddToCache(fileCodeKey, ftpFile, fileExpireHour);
LogHelper.GetLog(this).DebugFormat("断点续传内容MD5写入缓存:filename:‘{0}‘, fileContentMD5:‘{1}‘", oldFileName, fileMd5Name);
fileOperatorService.SetSuccessResponse(response, fileUploadResult, FileOperatorStatusCode.SaveSuccess);
response.code = 0;
response.error = string.Empty;
}
#endregion
}
else
{
var filePath = FtpOperationUtil.GetFileDefaultPath(((BusinessFileType)businessType).ToString());
// 判断缓存
//var fileCodeKey = (ConstVal.foreverFilePre + businessType + ":" + fileMd5Name).ToUpper();
var fileCodeKey = (ConstVal.foreverFilePre + fileMd5Name).ToUpper();
FtpFile ftpFile = new FtpFile();
using (var cache = new StackExchangeRedis())
{
ftpFile = cache.Get<FtpFile>(fileCodeKey);
}
if (businessType == (int)BusinessFileType.ChatFile && ExchangeFilePath(ftpFile.filePath) != ExchangeFilePath(filePath))
{
FtpOperationUtil.MoveFile(ExchangeFilePath(oldFilePath + "/" + fileMd5Name), filePath);
//// 更新文件信息
//fileOperatorService.UpdateChatFile(businessType, fileMd5Name, filePath);
ftpFile.filePath = filePath;
fileOperatorService.AddToCache(fileCodeKey, ftpFile);
}
}
}
#endregion
}
}
catch (Exception e)
{
response.code = -200;
response.error = e.Message.ToString();
LogHelper.GetLog(this).Error("异步上传异常:" + e.Message.ToString() + e.StackTrace);
}
return response;
});
#endregion
return Json(response);
}

/// <summary>
/// 上传文件根据业务类型上传进行断点续传
/// </summary>
/// <returns></returns>
[IgnoreRequestStreamLog]
//[HttpPost, Route("ResumeUploadFile")]
[HttpPost]
public async Task<JsonResult<BreakUploadResponse>> ResumeUploadFile()
{
// Check whether the POST operation is MultiPart?
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var response = new BreakUploadResponse
{
breakUploadResult = new List<BreakUploadResult>(),
};
#region 取流文件,然后决定是否保存文件至Ftp
var provider = new MultipartFormDataMemoryStreamProvider();
// Read the form and file data.
BreakUploadResponse uploadResponse = await Request.Content.ReadAsMultipartAsync(provider)
.ContinueWith<BreakUploadResponse>(o =>
{
try
{
#region 主文件信息
if (!provider.FormData.AllKeys.Any(a => a.Equals("fileName", StringComparison.CurrentCultureIgnoreCase)) ||
!provider.FormData.AllKeys.Any(a => a.Equals("blockCount", StringComparison.CurrentCultureIgnoreCase)) ||
!provider.FormData.AllKeys.Any(a => a.Equals("totalSize", StringComparison.CurrentCultureIgnoreCase)))
{
response.code = -400;
response.error = "请求参数错误,需包含主文件信息";
return response;
}
else
{
response.fileName = provider.FormData.GetValues(provider.FormData.AllKeys.Where(w => w.Equals("fileName", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()).FirstOrDefault();
response.fileCode = response.fileName;
int blockCount = 0;
var blockCountKey = provider.FormData.GetValues(provider.FormData.AllKeys.Where(w => w.Equals("blockCount", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()).FirstOrDefault();
int.TryParse(blockCountKey, out blockCount);
response.blockCount = blockCount;
long totalSize;
var totalSizeKey = provider.FormData.GetValues(provider.FormData.AllKeys.Where(w => w.Equals("totalSize", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()).FirstOrDefault();
long.TryParse(totalSizeKey, out totalSize);
response.totalSize = totalSize;
if (blockCount == 0 || totalSize == 0L)
{
response.code = -400;
response.error = "请求参数错误,主文件信息错误";
return response;
}
}
int businessType = 0;
if (!provider.FormData.AllKeys.Any(a => a.Equals("businessType", StringComparison.CurrentCultureIgnoreCase)))
{
response.code = -(int)FileOperatorStatusCode.ParameterError;
response.error = "请求参数错误,请求参数不包含业务类型信息!";
return response;
}
else
{
var businessTypeKey = provider.FormData.GetValues(provider.FormData.AllKeys.Where(w => w.Equals("businessType", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()).FirstOrDefault();
Int32.TryParse(businessTypeKey, out businessType);//从表单中获取到上传文件业务类型,输出赋值给busnissType
var bustype = (BusinessFileType)businessType;
var isSuccess = false;
switch (bustype)
{
case BusinessFileType.HeadPortrait:
case BusinessFileType.DynamicFile:
case BusinessFileType.ChatFile:
case BusinessFileType.AlbumFile:
case BusinessFileType.LabelFile:
case BusinessFileType.SpaceFile:
case BusinessFileType.AdFile:
isSuccess = true;
break;
default:
isSuccess = false;
break;
}
if (!isSuccess)
{
return new BreakUploadResponse
{
code = -(int)FileOperatorStatusCode.ParameterError,
error = "请求参数错误,不支持的业务类型!"
};
}
}
#endregion

foreach (MultipartFileData file in provider.FileData)
{
var breakUploadResult = new BreakUploadResult();
#region 检查文件块信息
foreach (var para in file.Headers.ContentDisposition.Parameters)
{
if (para.Name.ToLower().Equals("filename"))
{
breakUploadResult.fileName = para.Value.Trim(‘"‘);
if (breakUploadResult.fileName.ToUpper() != response.fileName.ToUpper())//上传文件名称不等于请求文件名称,返回请求参数错误
{
response.code = -400;
response.error = "请求参数错误,主文件信息与文件块信息不一致";
return response;
}
continue;
}
if (para.Name.ToLower().Equals("blocknum"))
{
int blockNum;
int.TryParse(para.Value.Trim(‘"‘), out blockNum);
breakUploadResult.blockNum = blockNum;
continue;
}
if (para.Name.ToLower().Equals("blocksize"))
{
long blockSize;
long.TryParse(para.Value.Trim(‘"‘), out blockSize);
breakUploadResult.blockSize = blockSize;
continue;
}
}
if (breakUploadResult.blockNum == 0 || breakUploadResult.blockSize == 0L)
{
response.code = -400;
response.error = "请求参数错误,文件块信息错误";
return response;
}
#endregion

#region 检查扩展名黑白名单 文件主信息与明细信息
var fileExt = response.fileName.Substring(response.fileName.LastIndexOf(‘.‘) + 1)?.ToLower();
if (string.IsNullOrWhiteSpace(fileExt) || !FileExtensionUtil.IsWhiteContains(fileExt) || FileExtensionUtil.IsBlackContains(fileExt))
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.NotSupportType);
continue;
}
#endregion

using (var stream = ((MultipartFileDataStream)file).Stream)
{
#region 检查文件大小
breakUploadResult.actualSize = stream.Length;
// 最大文件大小
if (breakUploadResult.actualSize <= 0)//如果当前上传文件大小小于0,返回请选择上传文件信息
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.NotExist);
continue;
}
else if (breakUploadResult.actualSize > maxSizeUploadFile)//如果当前上传文件当前大小不等于上传文件最大大小,返回上传文件大小超过限制
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.OverSize);
continue;
}
else if (breakUploadResult.actualSize != breakUploadResult.blockSize)//如果当前上传文件大小不等于请求文件块大小,返回数据文件不完整
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.NotFull);
continue;
}
#endregion

#region 根据文件名查询文件是否已存在 (需调用方把文件名改为文件内容MD5,再加后缀)
var ftpFile = new FtpFile();
FileBreakPoint fileBreakpoint = new FileBreakPoint
{
blocks = new List<FileBlockPoint>()
};
var fileContentMD5 = response.fileName.Substring(0, response.fileName.LastIndexOf(‘.‘)).ToUpper();
var fileMd5Name = fileContentMD5 + "." + fileExt;
//var fileMd5Key = (ConstVal.foreverFilePre + businessType + ":" + fileMd5Name).ToUpper();
var fileMd5Key = (ConstVal.foreverFilePre + fileMd5Name).ToUpper();
var fileBreakPointKey = (ConstVal.breakBlockFilePre + fileMd5Name).ToUpper();
// 查询缓存
using (var cache = new StackExchangeRedis())
{
ftpFile = cache.Get<FtpFile>(fileMd5Key);
fileBreakpoint = cache.Get<FileBreakPoint>(fileBreakPointKey);
} // 尽早释放using
#region 数据库判断文件是否已经存在
var oldFilePath = string.Empty;
var fileMd5IsExist = fileOperatorService.CheckFileIsExist(fileMd5Name, businessType, out oldFilePath);
#endregion
if (fileMd5IsExist)
{
breakUploadResult = null;
fileOperatorService.SetBlockSuccessResponse(response, breakUploadResult, FileOperatorStatusCode.AlreadyExists);
LogHelper.GetLog(this).ErrorFormat("文件‘{0}‘已存在", response.fileName);
response.isFinished = true;
if (ftpFile != null)
{
// 聊天文件移动位置
var filePath = FtpOperationUtil.GetFileDefaultPath(((BusinessFileType)businessType).ToString());
if ((BusinessFileType)businessType == BusinessFileType.ChatFile && ExchangeFilePath(ftpFile.filePath) != ExchangeFilePath(filePath))
{
FtpOperationUtil.MoveFile(ExchangeFilePath(ftpFile.filePath), filePath);
//var fileCodeKey = (ConstVal.foreverFilePre + businessType + ":" + fileMd5Name).ToUpper();
var fileCodeKey = (ConstVal.foreverFilePre + fileMd5Name).ToUpper();
ftpFile.filePath = filePath;
fileOperatorService.AddToCache(fileCodeKey, ftpFile);
}
}
return response;
}
else
{
if ((BusinessFileType)businessType != BusinessFileType.ChatFile)
{
var dbResult = fileOperatorService.GetFile(businessType, fileMd5Name);//获取文件信息
if (dbResult != null && dbResult.Count > 0)//如果FTP服务器存在该文件,则返回文件已存在信息接口
{
breakUploadResult = null;
fileOperatorService.SetBlockSuccessResponse(response, breakUploadResult, FileOperatorStatusCode.AlreadyExists);
response.isFinished = true;
return response;
}
}
}
if (fileBreakpoint == null)
{
fileBreakpoint = new FileBreakPoint
{
blocks = new List<FileBlockPoint>(),
fileName = response.fileName,
fileSize = response.totalSize,
nodeName = ftpServerNodeName,
filePath = FtpOperationUtil.rootPath + "/" + ((BusinessFileType)businessType).ToString() + "/" + dateTimePath,
fileCode = fileMd5Name,
blockCount = response.blockCount
};
var fileBlockPoint = new FileBlockPoint
{
fileName = breakUploadResult.fileName,
blockNum = breakUploadResult.blockNum,
blockSize = breakUploadResult.blockSize,
isFinished = false
};
fileBreakpoint.blocks.Add(fileBlockPoint);
// 加入缓存
fileOperatorService.AddToCache(fileBreakPointKey, fileBreakpoint);
}
else
{
// 当前块已经上传完成,返回上传文件已存在
if (fileBreakpoint.blocks.Any(w => w.blockNum == breakUploadResult.blockNum && w.isFinished == true))
{
fileOperatorService.SetBlockSuccessResponse(response, breakUploadResult, FileOperatorStatusCode.AlreadyExists);
continue;
}
// 当前块未上传完成,把信息添加到断点续传
if (!fileBreakpoint.blocks.Any(w => w.blockNum == breakUploadResult.blockNum))
{
var fileBlockPoint = new FileBlockPoint
{
fileName = breakUploadResult.fileName,
blockNum = breakUploadResult.blockNum,
blockSize = breakUploadResult.blockSize,
isFinished = false
};
fileBreakpoint.blocks.Add(fileBlockPoint);
}
// 加入缓存
fileOperatorService.AddToCache(fileBreakPointKey, fileBreakpoint);
}
#endregion

#region Ftp操作
#region 上传Ftp服务器
// 获取完整存储路径(含 ftp:// 的绝对路径不含文件名)
var fileBlockCompleteSize = FtpOperationUtil.UploadFileBlockOrBroken(stream, fileMd5Name + "." + breakUploadResult.blockNum, ExchangeFilePath(fileBreakpoint.filePath));//上传文件到FTP服务器(单次上传或续传)
if (fileBlockCompleteSize == 0L)
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.SaveFileDefeated);//上传文件失败
continue;
}
else
{
if (fileBlockCompleteSize == breakUploadResult.blockSize)//当上传文件大小等于上传文件块大小
{
var fileBlockPoint = new FileBlockPoint();
fileBlockPoint = fileBreakpoint.blocks.Where(w => w.blockNum == breakUploadResult.blockNum).FirstOrDefault();
fileBreakpoint.blocks.Remove(fileBlockPoint);
fileBlockPoint.isFinished = true;
fileBreakpoint.blocks.Add(fileBlockPoint);
// 加入缓存
fileOperatorService.AddToCache(fileBreakPointKey, fileBreakpoint);
fileOperatorService.SetBlockSuccessResponse(response, breakUploadResult, FileOperatorStatusCode.SaveSuccess);//当前文件上传成功
}
else
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.SaveFileDefeated);
continue;
}
}
#endregion

#region 全部上传完成则合并各个部分内容MD5缓存
if (fileBreakpoint.blocks.All(w => w.isFinished == true) && (fileBreakpoint.blocks.Count == response.blockCount) && (fileBreakpoint.blocks.Sum(s => s.blockSize) == response.totalSize))
{
response.isFinished = true;
// 合并文件
List<string> fileFullPaths = fileBreakpoint.blocks.OrderBy(a => a.blockNum).Select(s => s.fileName + "." + s.blockNum).ToList();
var remoteFilepath = ExchangeFilePath(fileBreakpoint.filePath);//转化文件路径
// 文件合并后需返回 MD5是否一致TODO
FtpOperationUtil.FileCombine(fileFullPaths, response.fileName, remoteFilepath, isCombineDelete ?? false);
if ((BusinessFileType)businessType != BusinessFileType.ChatFile)
{
// 先写入数据库
var dbResult = fileRepository.AddFile(response.fileName, businessType, fileMd5Name, ConfigUtil.GetConfigStr("FtpServerIP"), fileBreakpoint.filePath, 0);
if (dbResult < 0)
{
fileOperatorService.SetBlockErrorResponse(response, breakUploadResult, FileOperatorStatusCode.DbSaveFileDefeated);
return response;
}
}
// 删除断点续传缓存
using (var cache = new StackExchangeRedis())
{
cache.Remove(fileBreakPointKey);
}
// 加入内容缓存
ftpFile = new FtpFile
{
fileName = response.fileName,
nodeName = ftpServerNodeName,
businessType = businessType, // 文件上传业务类型
filePath = FtpOperationUtil.rootPath + "/" + ((BusinessFileType)businessType).ToString() + "/" + dateTimePath,
fileCode = fileMd5Name
};
//var fileCodeKey = (ConstVal.foreverFilePre + businessType + ":" + fileMd5Name).ToUpper();
var fileCodeKey = (ConstVal.foreverFilePre + fileMd5Name).ToUpper();
//加入缓存
fileOperatorService.AddToCache(fileCodeKey, ftpFile);
LogHelper.GetLog(this).DebugFormat("写入内容MD5缓存:filename:‘{0}‘, fileContentMD5:‘{1}‘", response.fileName + breakUploadResult.blockNum, fileMd5Name);
response.code = 0;
response.error = string.Empty;
}
#endregion
#endregion
}
}
}
catch (Exception e)
{
response.code = -200;
response.error = e.Message.ToString();
LogHelper.GetLog(this).Error("异步上传异常:" + e.Message.ToString() + e.StackTrace);
}
return response;
});
#endregion
return Json(response);
}

/// <summary>
/// 图片上传压缩(流方式)
/// </summary>
/// <returns></returns>
[IgnoreRequestStreamLog]
//[HttpPost, Route("CompressImageUploadFile")]
[HttpPost]
public async Task<JsonResult<UploadImageResponse>> CompressImageUploadFile()
{
// Check whether the POST operation is MultiPart?
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var response = new UploadImageResponse
{
uploadResult = new List<UploadImageResult>(),
// 文件保存失败
code = (int)FileOperatorStatusCode.SaveFileDefeated,
error = FileOperatorStatusCode.SaveFileDefeated.GetEnumText()
};
#region 取流文件,然后决定是否保存文件至Ftp
var provider = new MultipartFormDataMemoryStreamProvider();
// Read the form and file data.
UploadImageResponse uploadResponse = await Request.Content.ReadAsMultipartAsync(provider)
.ContinueWith(o =>
{
try
{
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
int businessType = 0;//上传文件业务类型
if (!provider.FormData.AllKeys.Any(a => a.Equals("businessType", StringComparison.CurrentCultureIgnoreCase)))
{
response.code = -(int)FileOperatorStatusCode.ParameterError;
response.error = "请求参数错误,请求参数不包含业务类型信息!";
return response;
}
else
{
var businessTypeKey = provider.FormData.GetValues(provider.FormData.AllKeys.Where(w => w.Equals("businessType", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()).FirstOrDefault();
Int32.TryParse(businessTypeKey, out businessType);//从表单中获取到上传文件业务类型,输出赋值给busnissType
}
var imageUploadResult = new UploadImageResult();
imageUploadResult.compressFile = new List<PerFileInfo>();
//接收文件
var oldFileName = file.Headers.ContentDisposition.FileName.Trim(‘"‘);//获取上传文件实际的文件名
var fileExt = oldFileName.Substring(oldFileName.LastIndexOf(‘.‘) + 1)?.ToLower();
imageUploadResult.fileName = oldFileName;
#region 检查扩展名黑白名单
if (string.IsNullOrWhiteSpace(fileExt) || !FileExtensionUtil.IsImageNotCludeGif(fileExt))
{
fileOperatorService.SetErrorResponse(response, imageUploadResult, FileOperatorStatusCode.NotSupportType);
continue;
}
#endregion
using (var stream = ((MultipartFileDataStream)file).Stream)
{
#region 检查文件大小
// 最大文件大小
var fileLength = stream.Length;
if (fileLength <= 0)
{
fileOperatorService.SetErrorResponse(response, imageUploadResult, FileOperatorStatusCode.NotExist);
continue;
}
else if (fileLength > maxSizeUploadFile)
{
fileOperatorService.SetErrorResponse(response, imageUploadResult, FileOperatorStatusCode.OverSize);
continue;
}
#endregion
//大图片MD5
var largeImageMD5 = FileMD5HashCodeUtil.GetFileContentMD5HashCode(stream);
var beforeUploadImageInfos = fileOperatorService.CheckImageCompressEffect(stream);
foreach (var uploadImageInfo in beforeUploadImageInfos)
{
var pressImage = new PerFileInfo();
var imageMd5Name = uploadImageInfo.fileMd5Code + "." + fileExt;
pressImage.imageCode = imageMd5Name;
pressImage.imageSize = uploadImageInfo.fileSize;
pressImage.imageHeight = uploadImageInfo.height;
pressImage.imageWidth = uploadImageInfo.width;
pressImage.imageSizeType = uploadImageInfo.imageSizeType;

#region 根据文件内容MD5查询文件是否已存在
var oldFilePath = string.Empty;
var fileMd5IsExist = fileOperatorService.CheckFileIsExist(imageMd5Name, businessType, out oldFilePath);
if (fileMd5IsExist)
{
fileMd5IsExist = true;
imageUploadResult.compressFile.Add(pressImage);
continue;
}
#endregion
#region Ftp操作 上传Ftp
if (!fileMd5IsExist)
{
#region 上传Ftp服务器
// 获取完整存储路径(不含 ftp:// serverip)
var filePath = fileOperatorService.SaveFileToFtp(uploadImageInfo.stream, imageMd5Name, FtpOperationUtil.GetFileDefaultPath(((BusinessFileType)businessType).ToString()));
uploadImageInfo.stream.Position = 0;
if (string.IsNullOrEmpty(filePath))
{
LogHelper.GetLog(this).Error("返回路径为空");
pressImage.code = FileOperatorStatusCode.SaveFileDefeated;
imageUploadResult.compressFile.Add(pressImage);
continue;
}
#endregion
#region 不存在则存入内容MD5缓存
if ((BusinessFileType)businessType != BusinessFileType.ChatFile)
{
// 先写入数据库
var dbResult = fileOperatorService.AddFile(businessType, oldFileName, imageMd5Name, filePath);
if (!dbResult)
{
LogHelper.GetLog(this).Error("写入数据库失败");
pressImage.code = FileOperatorStatusCode.DbSaveFileDefeated;
imageUploadResult.compressFile.Add(pressImage);
continue;
}
}
imageUploadResult.compressFile.Add(pressImage);
var ftpFile = new FtpFile();
ftpFile = new FtpFile
{
fileName = oldFileName,
businessType = businessType,
nodeName = ftpServerNodeName,
filePath = filePath,
fileCode = imageMd5Name,
fileType = fileExt
};
int fileExpireHour = 0;
int.TryParse(ftpFileCacheExpireHour, out fileExpireHour);
if (fileExpireHour == 0)
{
fileExpireHour = 24;
}
//var fileCodeKey = (ConstVal.foreverFilePre + businessType + ":" + imageMd5Name).ToUpper();
var fileCodeKey = (ConstVal.foreverFilePre + imageMd5Name).ToUpper();
using (var cache = new StackExchangeRedis())
{
// 文件内容MD5存入缓存
cache.Set<FtpFile>(fileCodeKey, ftpFile, DateTime.Now.AddHours(fileExpireHour));
}
LogHelper.GetLog(this).DebugFormat("写入内容MD5缓存:filename:‘{0}‘, fileContentMD5:‘{1}‘", oldFileName, imageMd5Name);
#endregion
}
#endregion
}
if (imageUploadResult.compressFile != null && imageUploadResult.compressFile.All(a => a.isSuccess == true))
{
fileOperatorService.SetSuccessResponse(response, imageUploadResult, FileOperatorStatusCode.SaveSuccess);
response.code = 0;
response.error = string.Empty;
}
else
{
FileOperatorStatusCode errorCode = FileOperatorStatusCode.UnDefined;
if (imageUploadResult.compressFile != null && imageUploadResult.compressFile.Any(a => a.isSuccess == false))
{
errorCode = imageUploadResult.compressFile.Where(a => a.isSuccess == false).FirstOrDefault().code;
}
fileOperatorService.SetErrorResponse(response, imageUploadResult, errorCode);
}
}
}
}
catch (Exception e)
{
response.code = -200;
response.error = e.Message.ToString();
LogHelper.GetLog(this).Error("异步上传异常:" + e.Message.ToString() + e.StackTrace);
}
return response;
});
#endregion
return Json(response);
}
#endregion

#region 文件下载
/// <summary>
/// 文件下载
/// </summary>
/// <returns></returns>
//[HttpGet, Route("DownloadFile/{fileCode}/{businessType}")]
[IgnoreResponseStreamLog]
[HttpGet]
public async Task<HttpResponseMessage> DownloadFile(string fileCode, int? businessType = null)
{
fileCode = fileCode?.Replace("fileCode=", "");
var taskResult = await Task.Run(async () =>
{
var response = Request.CreateResponse();
try
{
if (string.IsNullOrWhiteSpace(fileCode))
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest, Content = new StringContent("{ \"error\":\"请求参数不能为空\" }", Encoding.GetEncoding("UTF-8"), "application/json") };
}
int fileMd5Len = 0;
fileCode = FileExtensionUtil.FormatString(fileCode, out fileMd5Len);
if (fileMd5Len != 32)
{
if (fileMd5Len == -1)
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest, Content = new StringContent("{ \"error\":\"请求参数不含后缀名\" }", Encoding.GetEncoding("UTF-8"), "application/json") };
}
return new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest, Content = new StringContent("{ \"error\":\"请求参数不是MD5码\" }", Encoding.GetEncoding("UTF-8"), "application/json") };
}
//var fileMd5Key = (ConstVal.foreverFilePre + businessType + ":" + fileCode).ToUpper();
var fileMd5Key = (ConstVal.foreverFilePre + fileCode).ToUpper();
var fileContentMD5 = fileCode.Substring(0, fileCode.LastIndexOf(‘.‘)).ToUpper();
var fileExt = fileCode.Substring(fileCode.LastIndexOf(‘.‘) + 1);
var fileMd5Name = fileContentMD5 + "." + fileExt;
// Ftp上文件相对路径
var remoteFilePath = string.Empty;
#region 判断文件是否存在
var fileMd5IsExist = fileOperatorService.CheckFileIsExist(fileMd5Name, businessType, out remoteFilePath);
if (!fileMd5IsExist)
{
return new HttpResponseMessage { Content = new StringContent("{ \"error\":\"文件不存在\" }", Encoding.GetEncoding("UTF-8"), "application/json") };
}
#endregion
#region Range 是否为断点续传
long startPosition = -1;
long endPosition = -1;
var contentRange = string.Empty;
long fileSize = 0;
if (Request.Headers.Contains("Range"))
{
contentRange = Request.Headers.Range.Ranges.FirstOrDefault().ToString();
//bytes 10000-19999/1157632
if (!string.IsNullOrEmpty(contentRange))
{
contentRange = contentRange.Replace("bytes", "").Trim();
contentRange = contentRange.Substring(0, contentRange.IndexOf("/") < 0 ? contentRange.Length : contentRange.IndexOf("/"));
string[] ranges = contentRange.Split(‘-‘);
long.TryParse(ranges[0], out startPosition);
long.TryParse(ranges[1], out endPosition);
fileSize = FtpOperationUtil.GetFileSize(ExchangeFilePath(remoteFilePath) + "/" + fileMd5Name);
endPosition = endPosition == 0 ? fileSize : endPosition;
if (startPosition >= endPosition)
{
startPosition = 0;
endPosition = 0;
LogHelper.GetLog().Info("下载节点大于等于结束节点");
}
if (endPosition >= fileSize)
{
startPosition = 0;
endPosition = 0;
LogHelper.GetLog().Info("结束节点大于总大小");
}
}
}
#endregion
var mimeType = Extension2MimeType.ResourceManager.GetString(fileExt?.ToLower());
if (string.IsNullOrWhiteSpace(mimeType))
{
mimeType = "application/octet-stream";
}
if (mimeType.StartsWith("video"))
{
#region Ftp下载
var video = new VideoStream(remoteFilePath + "/" + fileMd5Name);
Action<Stream, HttpContent, TransportContext> send = video.WriteToStream;
response.Content = new PushStreamContent(send, new MediaTypeHeaderValue(mimeType));
//response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = fileMd5Name
};
response.Headers.Add("Cache-Control", $"{cacheTime}");
//调用异步数据推送接口
return response;
#endregion
}
else
{
#region Ftp下载
Stream outputStream = new MemoryStream();
if (outputStream == null || outputStream.Length == 0)
{
//if (startPosition >= 0 && endPosition != 0)
if (endPosition > 0)
{
//outputStream = FtpOperationUtil.FtpBrokenDownload(ExchangeFilePath(remoteFilePath + "/" + fileMd5Name), true, startPosition, endPosition);
outputStream = await FtpOperationUtil.FtpAsyncBrokenDownload(ExchangeFilePath(remoteFilePath + "/" + fileMd5Name), true, startPosition, endPosition);
}
else
{
//outputStream = FtpOperationUtil.FtpDownload(ExchangeFilePath(remoteFilePath + "/" + fileMd5Name));
outputStream = await FtpOperationUtil.FtpAsyncDownload(ExchangeFilePath(remoteFilePath + "/" + fileMd5Name)) as MemoryStream;
}
}
if (outputStream != null)
{
outputStream.Position = 0; // 重置下流读取位置
response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(outputStream)
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
//response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = fileMd5Name
};
if (!string.IsNullOrEmpty(contentRange))
{
response.Content.Headers.Add("Content-Range", string.Format("bytes {0}-{1}/{2}", startPosition, endPosition, fileSize));
}
response.Headers.Add("Cache-Control", $"{cacheTime}");
return response;
}
else
{
LogHelper.GetLog(this).DebugFormat("不存在文件名MD5:‘{0}‘", fileCode);
return new HttpResponseMessage { Content = new StringContent("{ \"error\":\"文件不存在\"}", Encoding.GetEncoding("UTF-8"), "application/json") };
}
#endregion
}
}
catch (Exception e)
{
LogHelper.GetLog(this).Error(e.Message.ToString() + e.StackTrace);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
});
return taskResult;
}
#endregion

#region 检查是否存在
/// <summary>
/// 检查是否存在(返回文件大小、是否断点续传等信息)
/// </summary>
/// <param name="fileCode"></param>
/// <param name="businessType"></param>
/// <param name="isUpdateExpireTime"></param>
/// <returns></returns>
//[HttpGet, Route("IsFileExist/{fileCode}/{businessType}/{isUpdateExpireTime:bool=false}")]
[HttpGet]
public async Task<JsonResult<FileExistResponse>> IsFileExist(string fileCode)
{
int businessType = 3;
bool isUpdateExpireTime = true;
fileCode = fileCode?.Replace("fileCode=", "");
FileExistResponse response = new FileExistResponse();
response.blocks = new List<BlockPoint>();
response.breakFile = new BreakFileInfo();
if (string.IsNullOrWhiteSpace(fileCode))
{
response.error = "请求参数不能为空";
return Json(response);
}
int fileMd5Len = 0;
fileCode = FileExtensionUtil.FormatString(fileCode, out fileMd5Len);
if (fileMd5Len != 32)
{
if (fileMd5Len == -1)
{
response.error = "请求参数不含后缀名";
}
else
{
response.error = "请求参数不是MD5码";
}
return Json(response);
}
var task = Task.Run(() =>
{
try
{
//var fileMd5Key = (ConstVal.foreverFilePre + businessType + ":" + fileCode).ToUpper();
var fileMd5Key = (ConstVal.foreverFilePre + fileCode).ToUpper();
var ftpFile = new FtpFile();
var fileBreakPoint = new FileBreakPoint();
// 判断缓存是否存在
var fileContentMD5 = fileCode.Substring(0, fileCode.LastIndexOf(‘.‘)).ToUpper();
var fileExt = fileCode.Substring(fileCode.LastIndexOf(‘.‘) + 1);
var fileMd5Name = fileContentMD5 + "." + fileExt;
var oldFilePath = string.Empty;
var fileMd5IsExist = fileOperatorService.CheckFileIsExist(fileCode, businessType, out oldFilePath);
if (!fileMd5IsExist)
{
var dbResponse = fileRepository.GetFile(0, string.Empty, fileCode);
if (dbResponse.code == 0)
{
fileMd5IsExist = true;
oldFilePath = dbResponse.filePath;
}
else
{
//var fileBreakResumeKey = (ConstVal.breakResumeFilePre + businessType + ":" + fileCode).ToUpper();
var fileBreakResumeKey = (ConstVal.breakResumeFilePre + fileCode).ToUpper();
using (ICachable cache = new StackExchangeRedis())
{
ftpFile = cache.Get<FtpFile>(fileBreakResumeKey);
}
if (ftpFile != null)
{
var breakResumeFileSize = FtpOperationUtil.GetFileSize(ExchangeFilePath(ftpFile.filePath) + "/" + fileMd5Name);
response.isBreakResume = true;
response.breakFile = new BreakFileInfo();
response.fileCode = ftpFile.fileCode;
response.breakFile.fileCode = ftpFile.fileCode;
response.breakFile.fileSize = breakResumeFileSize;
}
else
{
// 是否分块续传
var fileBreakPointKey = (ConstVal.breakBlockFilePre + fileCode).ToUpper();
using (ICachable cache = new StackExchangeRedis())
{
fileBreakPoint = cache.Get<FileBreakPoint>(fileBreakPointKey);
}
if (fileBreakPoint != null)
{
response.isBreakPoint = true;
response.fileCode = fileBreakPoint.fileCode;
response.fileSize = fileBreakPoint.fileSize;
//response.blocksFullSize = fileBreakPoint.fileSize;
foreach (var block in fileBreakPoint.blocks)
{
// 查询看Ftp文件大小
var blockFileSize = FtpOperationUtil.GetFileSize(ExchangeFilePath(fileBreakPoint.filePath) + "/" + fileBreakPoint.fileCode + "." + block.blockNum);
var blockpoint = new BlockPoint
{
fileName = block.fileName,
blockNum = block.blockNum,
blockSize = block.blockSize,
currentSize = blockFileSize,
isFinished = block.isFinished
};
response.blocks.Add(blockpoint);
}
}
}
response.error = string.Format("‘{0}‘文件不存在", fileCode);
LogHelper.GetLog(this).DebugFormat("‘{0}‘文件不存在", fileCode);
return response;
}
}
ftpFile = new FtpFile
{
businessType = businessType,
nodeName = ftpServerNodeName,
filePath = oldFilePath,
fileCode = fileMd5Name
};
if (fileMd5IsExist && isUpdateExpireTime)
{
var filePath = FtpOperationUtil.GetFileDefaultPath(((BusinessFileType)businessType).ToString());
if (oldFilePath.Contains(((BusinessFileType)businessType).ToString()) && ExchangeFilePath(oldFilePath) != ExchangeFilePath(filePath))
{
FtpOperationUtil.MoveFile(ExchangeFilePath(oldFilePath + "/" + fileMd5Name), filePath);
ftpFile.filePath = filePath;
fileOperatorService.AddToCache(fileMd5Key, ftpFile);
}
}
// 获取Ftp文件大小
var fileSize = FtpOperationUtil.GetFileSize(ExchangeFilePath(ftpFile.filePath) + "/" + ftpFile.fileCode);
response.fileCode = ftpFile.fileCode;
response.fileSize = fileSize;
return response;
}
catch (Exception e)
{
response.error = "Api内部异常";
LogHelper.GetLog(this).Error(e.Message.ToString() + e.StackTrace);
return response;
}
});
var taskResult = await Task.WhenAll(task);
return Json(taskResult.FirstOrDefault());
}
#endregion

#region Private Method
/// <summary>
/// 转化文件路径
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private string ExchangeFilePath(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
return filePath;
var index = filePath.IndexOf(ftprootpath);
if (index < 0)
{
if (filePath.Contains("ftp://"))
{
return filePath;
}
else
{
return Path.Combine(FtpOperationUtil.ftpURI, filePath);
}
}
return Path.Combine(FtpOperationUtil.ftpURI, filePath.Substring(index));
}
#endregion Private Method
}

/// <summary>
///
/// </summary>
public class VideoStream
{
private readonly string _filefullname;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="fileFullName"></param>
public VideoStream(string fileFullName)
{
_filefullname = fileFullName;
}
/// <summary>
///
/// </summary>
/// <param name="outputStream"></param>
/// <param name="content"></param>
/// <param name="context"></param>
public async void WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[2048];
var video = await FtpOperationUtil.FtpAsyncDownload(_filefullname);
var bytesRead = buffer.Length;
while (bytesRead == buffer.Length)
{
bytesRead = video.Read(buffer, 0, buffer.Length);
await outputStream.WriteAsync(buffer, 0, bytesRead);
}
}
catch (HttpException ex)
{
LogHelper.GetLog().Error(ex.Message + ex.StackTrace);
}
finally
{
outputStream.Close();
}
}
/// <summary>
/// 本地文件写入流
/// </summary>
/// <param name="outputStream"></param>
/// <param name="content"></param>
/// <param name="context"></param>
public async void LocalFileWriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[65536];
using (var video = File.Open(_filefullname, FileMode.Open, FileAccess.Read))
{
var length = (int)video.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
length -= bytesRead;
}
}
}
catch (HttpException ex)
{
LogHelper.GetLog().Error(ex.Message + ex.StackTrace);
}
finally
{
outputStream.Close();
}
}

}
}

原文地址:https://www.cnblogs.com/Nine4Cool/p/10540619.html

时间: 2024-10-07 04:17:28

WebApi 文件上传,断点上传,分块上传,断点下载,查询 (图片的直接预览,视频边加载边播放)的相关文章

H5上传前预览视频(结合 video标签 &amp;&amp;h5 fileApi)

2017/09/14 发布 js代码: // hTML5实现表单内的上传文件框,上传前预览视频,刷新预览video,使用HTML5 的File API, // 建立一个可存取到该file的url,一个空的video标签,ID为video0,把选择的文件显示在video标签中,实现视频预览功能. // 需要选择支持HTML API的浏览器. $("#video").change(function(){ var objUrl = getObjectURL(this.files[0]) ;

关于图片上传时选择图片以后生成图片预览

实现选择图片之后生成预览图 <script type="text/javascript">    //下面用于多图片上传预览功能    function setImagePreviews() {        //获取选择图片的对象        var docObj = document.getElementById("filesid");        //后期显示图片区域的对象        var dd = document.getElement

制作Appstore预览视频并上传

首先保证你的设备是mac,系统版本为10.10或以上. 工具: 浏览器:safari. 录像:QuickTime player 视频编辑:HandBrake 1.录制视频:打开QuickTime,选择[文件]-[新建影片录制]点击下拉框出现下面界面. 开始录制,注意视频应该在15-30秒之内. 2.编辑 这里主要是转换视频的编码格式.我们用到的工具是HandBrake,进入HandBrake界面导入我们刚才录制的视频.这里我们主要配置下面几个选项 ①FPS:我们用24就好了.②Audio的采样率

图片选择,预览及上传

记得以前老师教我们写项目,要实现图片上传的功能,我们都是先用一个input选好图片,然后单独做一个提交图片的按钮,点击按钮,使用form表单提交到后台,然后通过 // 获取上传的文件 HttpPostedFileBase file = Request.Files[0]; 这一行来获取上传到后台的文件,然后来验证上传的文件是不是图片,其实在前台,通过设置input属性,就可以限制我们只能选择图片文件了,当然,后台的验证也是不能少的, // 设置accept属性,限制能选择的文件类型为图片 <inp

百度webuploader图片点击预览和上传实现(angularjs版)

//注意,请自行下载最新版angularjs,webuploader.js, jquery.js <!DOCTYPE html><html> <head> <meta charset="utf-8"> <meta name="view" content="width=device-width;user-scalable=no;initial-scale=1.0"> <script

实现某某视频文件上传的接口并分块上传

直接上代码: public static bool httpPost(string url, ref CookieContainer cc, ref string dataToPost, ref string dataget, bool isChangeCookie = false) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.AllowAutoRedirect = false; reque

IOS开发-图片浏览器实例-UIImageView的使用-懒加载-plist文件的使用

一.本文概述 一个使用UIImageView.UILabel.UIButton实现的图片浏览器的实例,界面如图:   功能描述: 1. 点击左右箭头切换图片.图片描述.图片序号: 2.第一张图片时左箭头不能点击 3.最后一张图片时右箭头不能点击 4.点击设置按钮出现一个可设置的界面(上图中黄色背景的部分)可以设置模式和对图片的缩放 实现概述: 1.搭建UI界面,使用UIImageView控件显示图片 2. 监听个按钮的点击 3. 切换图片内容,描述,序号.背景色及UIImageView的tran

【纯干货】4年前想解决的事情,今天才实验成功:浏览器原生分块上传文件

第一份软件开发工作的第一个星期(不算做试用期的一个星期,无薪水试用).因为不是软件专业,也没有经过培训和相关工作经验.老板不放心,但还是让我试一试.做的第一件事情就是上传文件,实时看进度,并且上传后预览.预览的文件类型有word,ppt,excel,flash,视频按帧获取预览图.office文件是在服务器端转成html后显示出来. 做的还满意,就留下来了,后来就在那个公司待了两年. 没解决的事情: 上传大文件,分块上传,浏览器原生不支持,需要借助第三方插件.最根本的原因就是浏览器端的js考虑的

在使用 AjaxFileUpload 上传文件时,在项目发布到 iis 后,图片不能预览

在使用 AjaxFileUpload  上传文件时,图片已经上传成功了,在站点没有发布时,可以预览,可是在项目发布到 iis 后,图片就不能预览,在网上找了很多的方案也没解决,最后的解决方案如下: 1.开始运行 regedit 打开注册表,先备份注册表 2.找到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters 3.在 编辑 菜单上指向 新建 ,然后单击 DWORD 值 . 4.键入 EnableAggres