android编程如何实现边下载边播放?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android编程如何实现边下载边播放?相关的知识,希望对你有一定的参考价值。
参考技术A android边下载边播放的实现应该需要用到多线程,一个线程下载,一个线程播放,具体可以找一个现有的音乐播放器源码: http://www.mobizc.com/View.asp?ID=1253研究看看 参考技术B Player.java源码如下:[java] view plaincopyprint?
package com.videoplayer;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.SeekBar;
public class Player implements OnBufferingUpdateListener,
OnCompletionListener, MediaPlayer.OnPreparedListener,
SurfaceHolder.Callback
private int videoWidth;
private int videoHeight;
public MediaPlayer mediaPlayer;
private SurfaceHolder surfaceHolder;
private SeekBar skbProgress;
private Timer mTimer=new Timer();
public Player(SurfaceView surfaceView,SeekBar skbProgress)
this.skbProgress=skbProgress;
surfaceHolder=surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mTimer.schedule(mTimerTask, 0, 1000);
package com.videoplayer;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.SeekBar;
public class Player implements OnBufferingUpdateListener,
OnCompletionListener,MediaPlayer.OnPreparedListener,
SurfaceHolder.Callback
private int videoWidth;
private int videoHeight;
public MediaPlayer mediaPlayer;
private SurfaceHolder surfaceHolder;
private SeekBar skbProgress;
private Timer mTimer=new Timer();
public Player(SurfaceView surfaceView,SeekBar skbProgress)
test_videoplayer.java源码如下:
[java] view plaincopyprint?
package com.videoplayer;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
public class test_videoplayer extends Activity
private SurfaceView surfaceView;
private Button btnPause, btnPlayUrl, btnStop;
private SeekBar skbProgress;
private Player player;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView1);
btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl);
btnPlayUrl.setOnClickListener(new ClickEvent());
btnPause = (Button) this.findViewById(R.id.btnPause);
btnPause.setOnClickListener(new ClickEvent());
btnStop = (Button) this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(new ClickEvent());
skbProgress = (SeekBar) this.findViewById(R.id.skbProgress);
skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());
player = new Player(surfaceView, skbProgress);
class ClickEvent implements OnClickListener
@Override
public void onClick(View arg0)
if (arg0 == btnPause)
player.pause();
else if (arg0 == btnPlayUrl)
String url="http://daily3gp.com/vids/family_guy_penis_car.3gp";
player.playUrl(url);
else if (arg0 == btnStop)
player.stop();
class SeekBarChangeEvent implements SeekBar.OnSeekBarChangeListener
int progress;
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
// 原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
this.progress = progress * player.mediaPlayer.getDuration()
/ seekBar.getMax();
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onStopTrackingTouch(SeekBar seekBar)
// seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
player.mediaPlayer.seekTo(progress);
package com.videoplayer;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
public class test_videoplayer extends Activity
private SurfaceView surfaceView;
private Button btnPause, btnPlayUrl, btnStop;
private SeekBar skbProgress;
private Player player;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView1);
btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl);
btnPlayUrl.setOnClickListener(new ClickEvent());
btnPause = (Button) this.findViewById(R.id.btnPause);
btnPause.setOnClickListener(new ClickEvent());
btnStop = (Button) this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(new ClickEvent());
skbProgress = (SeekBar) this.findViewById(R.id.skbProgress);
skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());
player = new Player(surfaceView, skbProgress);
class ClickEvent implements OnClickListener
@Override
public void onClick(View arg0)
if (arg0 == btnPause)
player.pause();
else if (arg0 == btnPlayUrl)
String url=http://daily3gp.com/vids/family_guy_penis_car.3gp;
player.playUrl(url);
else if (arg0 == btnStop)
player.stop();
参考技术C Android边下载边播放的实现应该需要用到多线程,一个线程下载,一个线程播放,具体可以找一个现有的音乐播放器源码: http://www.mobizc.com/View.asp?ID=1253研究看看 参考技术D Android边下载边播放的实现应该需要用到多线程,一个线程下载,一个线程播放,具体可以找一个现有的音乐播放器源码: http://www.mobizc.com/View.asp?ID=1253研究看看 第5个回答 2015-08-10 音乐的下载很简单,但是要实现音乐的边下载可以边播放就不那么简单了
详情点击http://www.blogjava.net/bingle/archive/2010/02/09/266872.html
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();
}
}
}
}
以上是关于android编程如何实现边下载边播放?的主要内容,如果未能解决你的问题,请参考以下文章
音视频开发之旅(49)-边缓存边播放之AndroidVideoCache
音视频开发之旅(49)-边缓存边播放之AndroidVideoCache
Android视频播放器屏幕左侧边随手指上下滑动亮度调节变暗变亮原理实现:后续改进