最近在写关于音视频播放的案例,所以就趁机会研究了一下AVPlayer的内容。我封装的目前只能播放网络音视频。还未添加缓存,以后找机会研究一下再更新。代码中提供了音视频的上一曲、下一曲、暂停、开始、停止、单曲播放、顺序播放、随机播放等功能。代码写的不好,仅供参考~

代码接口文件

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>NS_ASSUME_NONNULL_BEGIN// 当前播放器的播放形式
typedef NS_ENUM(NSInteger, MediaPlayType) {MediaPlayTypeCycle,   ///< 顺序MediaPlayTypeSingle,  ///< 单曲循环MediaPlayTypeRandom   ///< 随机播放
};// 当前播放器的播放状态
typedef NS_ENUM(NSInteger, MediaPlayStatus) {MediaPlayStatusStop,        ///< 停止播放MediaPlayStatusPause,       ///< 暂停播放MediaPlayStatusPlaying      ///< 正在播放
};// 媒体加载状态
typedef NS_ENUM(NSInteger, MediaLoadStatus) {MediaLoadStatusReadyToPlay,  ///< 准备播放MediaLoadStatusUnknown,      ///< 未知MediaPlayStatusFailed        ///< 失败
};@class MediaPlyerManager;@protocol MediaPlyerManagerDelegate <NSObject>
@optional
// 数据加载状态 根据状态进行播放或其他操作
- (void)MediaPlayer:(MediaPlyerManager *)playerManager playerItemStatus:(MediaLoadStatus)status;
// 缓冲进度
- (void)MediaPlayer:(MediaPlyerManager *)playerManager netBufferValue:(CGFloat)value;
// 缓冲是否足够播放
- (void)MediaPlayer:(MediaPlyerManager *)playerManager bufferHasEnough:(BOOL)enough;
// 当前播放的时间
- (void)MediaPlayer:(MediaPlyerManager *)playerManager currentPlayTime:(NSString *)time currentPlayTimeValue:(CGFloat)value;
// 播放总时间
- (void)MediaPlayer:(MediaPlyerManager *)playerManager mediaEndTime:(NSString *)time mediaEndTimeValue:(CGFloat)value;
// 播放结束
- (void)MediaPlayerCurrentMediaPlayFinish:(MediaPlyerManager *)playerManager;
// 播放状态
- (void)MediaPlayer:(MediaPlyerManager *)playerManager playeStatus:(MediaPlayStatus)status;
// 获取数据切换时获取正在播放的URL和当前的index
- (void)MediaPlayer:(MediaPlyerManager *)playerManager currentUrl:(NSString *)url currentIndex:(NSInteger)index;
// 为了配合手机后台播放 实时获取播放的进度,总的时间,当前的index<通过index获取图片等信息>
- (void)MediaPlayer:(MediaPlyerManager *)playerManager currentProgressValue:(CGFloat)value totalValue:(CGFloat)totalValue currentIndex:(NSInteger)index;
@endtypedef MediaPlyerManager *(^playerCurrentTime)(NSString *time);@interface MediaPlyerManager : NSObject
@property (nonatomic, strong, readonly) AVPlayer                  *mediaPlayer;          ///< 播放器
@property (nonatomic, strong, readonly) AVPlayerItem              *meidaPlayerItem;      ///< 播放器的CurrentItem
@property (nonatomic, strong, readonly) NSMutableArray<NSString*> *dataUrlArray;         ///< 正在播放的列表数据
@property (nonatomic, assign, readonly) MediaPlayType              playType;             ///< 当前播放类型
@property (nonatomic, assign, readonly) MediaPlayStatus            playStatus;           ///< 当前播放状态
@property (nonatomic, assign, readonly) NSInteger                  currentIndex;         ///< 当前播放的索引
@property (nonatomic, assign, readonly) BOOL                       isPlaying;            ///< 是否在播放
@property (nonatomic, assign, readonly) CGFloat                    curentPlayTimeValue;  ///< 当前播放时间值
@property (nonatomic, copy, readonly) NSString                    *curentPlayTime;       ///< 当前播放时间
@property (nonatomic, assign, readonly) CGFloat                    endPlayTimeValue;     ///< 当前播放时间值
@property (nonatomic, copy, readonly) NSString                    *endPlayTime;          ///< 当前播放时间
+ (instancetype)defaultManager;/**列表播放 ⚠️<默认不自动播放>@param urls 文件路径数组@param delegate 回调代理@return MediaPlyerManager*/
- (MediaPlyerManager *)playerWithUrls:(NSArray<NSString *> *)urls actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate;/**单个音视频播放 ⚠️<默认不自动播放>@param url 文件路径@param delegate 回调代理@return MediaPlyerManager*/
- (MediaPlyerManager *)playerWithUrl:(NSString *)url actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate;/**开始播放*/
- (void)play;/**暂停播放*/
- (void)pause;/**停止播放*/
- (void)stop;/**下一曲*/
- (void)next;/**上一曲*/
- (void)previous;/**指定进度开始播放@param progress 进度百分比*/
- (void)setupPlayerSeekToProgress:(CGFloat)progress;/**制定播放类型@param type 类型*/
- (void)setupMediaPlayerType:(MediaPlayType)type;/**指定播放的index@param index 索引*/
- (void)setupPlayerIndex:(NSInteger)index;/**添加数据@param files 文件数组@param index 索引*/
- (void)insertMediaFile:(NSArray<NSString *> *)files atIndex:(NSInteger)index;/**移除全部数据*/
- (void)removeAllFiles;/**移除索引中的单个数据@param index 索引*/
- (void)removeObjectAtIndex:(NSInteger)index;/**设置锁屏样式@param coverImage 专辑图片@param size 显示大小@param title 标题@param author 专辑作者@param album 专辑名称@param currentTime 当前播放时间@param duration 播放总时长*/
- (void)setupLockScreenPlayInfo:(UIImage *)coverImageimageSize:(CGSize)sizetitle:(NSString *)titleahthor:(NSString *)authoralbum:(NSString *)albumcurrentPlayTime:(CGFloat)currentTimeduration:(CGFloat)duration;
@endNS_ASSUME_NONNULL_END
复制代码

代码实现文件

#import "MediaPlyerManager.h"
#import <MediaPlayer/MediaPlayer.h>@interface MediaPlyerManager ()
@property (nonatomic, strong, readwrite) NSMutableArray<NSString *> *dataUrlArray;
@property (nonatomic, strong, readwrite) AVPlayer                  *mediaPlayer;
@property (nonatomic, assign, readwrite) BOOL                      isPlaying;
@property (nonatomic, strong, readwrite) AVPlayerItem              *meidaPlayerItem;
@property (nonatomic, assign, readwrite) MediaPlayType             playeType;
@property (nonatomic, assign, readwrite) NSInteger                 currentIndex;
@property (nonatomic, assign, readwrite) MediaPlayStatus           playStatus;
@property (nonatomic, assign, readwrite) CGFloat                   curentPlayTimeValue;
@property (nonatomic, copy, readwrite  ) NSString                  *curentPlayTime;
@property (nonatomic, assign, readwrite) CGFloat                   endPlayTimeValue;
@property (nonatomic, copy, readwrite  ) NSString                  *endPlayTime;
@property (nonatomic, weak             ) id <MediaPlyerManagerDelegate> delegate;
@end@implementation MediaPlyerManager+ (instancetype)defaultManager {static dispatch_once_t onceToken;static MediaPlyerManager *manger;dispatch_once(&onceToken, ^{manger = [[MediaPlyerManager alloc] init];});return manger;
}#pragma mark - 初始化- (MediaPlyerManager *)playerWithUrl:(NSString *)url actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate {[self playerWithUrls:@[url] actionWithDelegate:delegate];return self;
}- (MediaPlyerManager *)playerWithUrls:(NSArray<NSString *> *)urls actionWithDelegate:(id<MediaPlyerManagerDelegate>)delegate {self.delegate         = delegate;self.currentIndex     = 0;self.dataUrlArray     = [NSMutableArray array];NSMutableArray *array = [NSMutableArray arrayWithCapacity:urls.count];for (NSString *urlStr in urls) {[array addObject:[self createPlayerItemWithUrl:urlStr]];[self.dataUrlArray addObject:urlStr];}self.playeType = MediaPlayTypeCycle;self.mediaPlayer = [[AVPlayer alloc] initWithPlayerItem:array.firstObject];self.meidaPlayerItem = self.mediaPlayer.currentItem;[self getCurrentIndex:self.currentIndex];[self addObserver];__weak typeof(self) weakself = self;[self.mediaPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {NSString *currentString = [weakself getStringFromCMTime:time];weakself.curentPlayTime = currentString;weakself.curentPlayTimeValue = (CGFloat)time.value/time.timescale;
#pragma mark - 获取当前播放时间if (weakself.delegate && [weakself.delegate respondsToSelector:@selector(MediaPlayer:currentPlayTime:currentPlayTimeValue:)]) {[weakself.delegate MediaPlayer:weakself currentPlayTime:currentString currentPlayTimeValue:(CGFloat)time.value/time.timescale];}
#pragma mark - 实时获取播放信息if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:currentProgressValue:totalValue:currentIndex:)]) {[weakself.delegate MediaPlayer:weakself currentProgressValue:weakself.curentPlayTimeValue totalValue:weakself.endPlayTimeValue currentIndex:weakself.currentIndex];}}];return self;
}#pragma mark - 播放结束- (void)playFinish:(NSNotification *)notification {if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayerCurrentMediaPlayFinish:)]) {[self.delegate MediaPlayerCurrentMediaPlayFinish:self];}if (self.playeType == MediaPlayTypeSingle) {[self.mediaPlayer seekToTime:kCMTimeZero];[self play];} else {if (self.currentIndex < self.dataUrlArray.count - 1) {self.currentIndex += 1;} else {self.currentIndex = 0;}[self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];self.meidaPlayerItem = self.mediaPlayer.currentItem;[self getCurrentIndex:self.currentIndex];[self play];}
}#pragma mark - KVO- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary<NSKeyValueChangeKey,id> *)changecontext:(void *)context {AVPlayerItem *playerItem = object;if ([keyPath isEqualToString:@"status"]) {MediaLoadStatus status = [change[@"new"] integerValue];
#pragma mark - 获取媒体加载状态if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:playerItemStatus:)]) {[self.delegate MediaPlayer:self playerItemStatus:status];}} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {NSArray * timeRanges         = playerItem.loadedTimeRanges;CMTimeRange timeRange        = [timeRanges.firstObject CMTimeRangeValue];NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) \+ CMTimeGetSeconds(timeRange.duration);NSTimeInterval duration      = CMTimeGetSeconds(playerItem.duration);NSTimeInterval scale         = totalLoadTime/duration;#pragma mark - 获取媒体总时间if ((CGFloat)duration/scale >= 0) {self.endPlayTime = [self getStringFromCMTime:playerItem.duration];self.endPlayTimeValue = (CGFloat)duration/scale;if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:mediaEndTime:mediaEndTimeValue:)]) {[self.delegate MediaPlayer:self mediaEndTime:[self getStringFromCMTime:playerItem.duration] mediaEndTimeValue:(CGFloat)duration/scale];}}#pragma mark - 缓冲百分比if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:netBufferValue:)]) {[self.delegate MediaPlayer:self netBufferValue:scale];}} else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {#pragma mark - 缓冲不足够播放if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:bufferHasEnough:)]) {[self.delegate MediaPlayer:self bufferHasEnough:false];}} else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {#pragma mark - 缓冲足够播放if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:bufferHasEnough:)]) {[self.delegate MediaPlayer:self bufferHasEnough:true];}}
}#pragma mark - 开始播放- (void)play {[self.mediaPlayer play];[self getPlayStatus:MediaPlayStatusPlaying];
}#pragma mark - 暂停播放- (void)pause {[self.mediaPlayer pause];[self getPlayStatus:MediaPlayStatusPause];
}#pragma mark - 停止播放- (void)stop {[self.mediaPlayer replaceCurrentItemWithPlayerItem:nil];[self getPlayStatus:MediaPlayStatusStop];[self removeObserver];
}#pragma mark - 下一个- (void)next {if (self.playeType == MediaPlayTypeRandom) {self.currentIndex = (NSInteger)arc4random_uniform((int32_t)(self.dataUrlArray.count - 1));} else {if (self.currentIndex == self.dataUrlArray.count - 1) {self.currentIndex = 0;} else {self.currentIndex += 1;}}[self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];self.meidaPlayerItem = self.mediaPlayer.currentItem;[self getCurrentIndex:self.currentIndex];[self addObserver];
}#pragma mark - 上一个- (void)previous {if (self.playeType == MediaPlayTypeRandom) {self.currentIndex = (NSInteger)arc4random_uniform((int32_t)(self.dataUrlArray.count - 1));} else {if (self.currentIndex == 0) {self.currentIndex = self.dataUrlArray.count - 1;} else {self.currentIndex -= 1;}}[self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];self.meidaPlayerItem = self.mediaPlayer.currentItem;[self getCurrentIndex:self.currentIndex];[self addObserver];
}#pragma mark - 播放状态- (void)getPlayStatus:(MediaPlayStatus)status {if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:playeStatus:)]) {[self.delegate MediaPlayer:self playeStatus:status];}self.playStatus = status;if (status == MediaPlayStatusPlaying) {self.isPlaying = true;} else {self.isPlaying = false;}
}#pragma mark - 根据index进行回调- (void)getCurrentIndex:(NSInteger)index {if (self.delegate && [self.delegate respondsToSelector:@selector(MediaPlayer:currentUrl:currentIndex:)]) {[self.delegate MediaPlayer:self currentUrl:self.dataUrlArray[index] currentIndex:index];}
}#pragma mark - 设置播放进度百分比- (void)setupPlayerSeekToProgress:(CGFloat)progress {float timeValue = progress * CMTimeGetSeconds(self.mediaPlayer.currentItem.duration);[self.mediaPlayer seekToTime:CMTimeMake(timeValue, 1)];
}#pragma mark - 设置播放形式- (void)setupMediaPlayerType:(MediaPlayType)type {self.playeType = type;
}#pragma mark - 播放指定index的媒体- (void)setupPlayerIndex:(NSInteger)index {if (index > (self.dataUrlArray.count - 1)) {@throw [NSException exceptionWithName:@"越界错误" reason:@"index 不能超出URL数组的长度" userInfo:nil];return;}self.currentIndex = index;[self.mediaPlayer replaceCurrentItemWithPlayerItem:[self createPlayerItemWithUrl:self.dataUrlArray[self.currentIndex]]];self.meidaPlayerItem = self.mediaPlayer.currentItem;[self getCurrentIndex:self.currentIndex];
}#pragma mark - 插入数据- (void)insertMediaFile:(NSArray<NSString *> *)files atIndex:(NSInteger)index {for (NSString *urlStr in files) {NSInteger i = [files indexOfObject:urlStr];[self.dataUrlArray insertObject:urlStr atIndex:index + i];}if (index < self.currentIndex) {self.currentIndex += 1;}
}#pragma mark - 删除数据- (void)removeAllFiles {[self stop];[self.dataUrlArray removeAllObjects];self.dataUrlArray = [NSMutableArray array];self.currentIndex = 0;
}- (void)removeObjectAtIndex:(NSInteger)index {if (self.dataUrlArray.count == 1) {[self removeAllFiles];} else {[self.dataUrlArray removeObjectAtIndex:index];if (index == self.currentIndex) {if (index == 0) {self.currentIndex = 0;[self next];} else {self.currentIndex -= 1;}} else {if (self.currentIndex > index) {self.currentIndex -= 1;} }}
}#pragma mark - Utils- (NSString *)getStringFromCMTime:(CMTime)time {float currentTimeValue       = (CGFloat)time.value/time.timescale;NSDate * currentDate         = [NSDate dateWithTimeIntervalSince1970:currentTimeValue];NSCalendar *calendar         = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];NSInteger unitFlags          = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;NSDateComponents *components = [calendar components:unitFlags fromDate:currentDate];if (currentTimeValue >= 3600 ) {return [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)components.hour, (long)components.minute, (long)components.second];} else {return [NSString stringWithFormat:@"%02ld:%02ld", (long)components.minute, (long)components.second];}
}- (void)addObserver {// 监控状态属性[self.meidaPlayerItem addObserver:selfforKeyPath:@"status"options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew)context:nil];// 监控缓冲加载情况属性[self.meidaPlayerItem addObserver:selfforKeyPath:@"loadedTimeRanges"options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew)context:nil];// 监听缓冲不足够播放[self.meidaPlayerItem addObserver:selfforKeyPath:@"playbackBufferEmpty"options:NSKeyValueObservingOptionNewcontext:nil];// 监听缓冲足够播放[self.meidaPlayerItem addObserver:selfforKeyPath:@"playbackLikelyToKeepUp"options:NSKeyValueObservingOptionNewcontext:nil];// 获取是否播放结束[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(playFinish:)name:AVPlayerItemDidPlayToEndTimeNotificationobject:self.meidaPlayerItem];
}- (void)removeObserver {[[NSNotificationCenter defaultCenter] removeObserver:self];@try {[self.meidaPlayerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];[self.meidaPlayerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];[self.meidaPlayerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];[self.meidaPlayerItem removeObserver:self forKeyPath:@"status"];}@catch(NSException *exception) {NSLog(@"%@", exception);}
}#pragma mark - 设置锁屏样式- (void)setupLockScreenPlayInfo:(UIImage *)coverImageimageSize:(CGSize)sizetitle:(NSString *)titleahthor:(NSString *)authoralbum:(NSString *)albumcurrentPlayTime:(CGFloat)currentTimeduration:(CGFloat)duration {Class playingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");if (playingInfoCenter) {NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithBoundsSize:size requestHandler:^UIImage * _Nonnull(CGSize size) {return coverImage;}];[songInfo setObject:title forKey:MPMediaItemPropertyTitle];[songInfo setObject:author forKey:MPMediaItemPropertyArtist];[songInfo setObject:album forKey:MPMediaItemPropertyAlbumTitle];[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];[songInfo setObject:[NSNumber numberWithDouble:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];[songInfo setObject:[NSNumber numberWithDouble:duration] forKey:MPMediaItemPropertyPlaybackDuration];[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];}
}- (AVPlayerItem *)createPlayerItemWithUrl:(NSString *)url {return [AVPlayerItem playerItemWithURL:[NSURL URLWithString:url]];
}@end
复制代码

提问

本来相同使用AVQueuePlayer来进行列表播放的,但是当做单曲循环的时候遇到问题:通过通知监听播放完成,在通知的方法里进行具体操作,但是设置无效,直接播放的还是下一个文件。如果有人知道如何解决,帮忙回复一下。

转载于:https://juejin.im/post/5c7e2e78e51d454210514cc2

AVPlayer封装相关推荐

  1. iOS开发 - 用AVPlayer封装一个播放器

    因为GIF上传的时候一直失败,所以大家到Github查看效果图 https://github.com/codeliu6572/LHHVideoPlayer 由于亮度和音量只能在真机上显现效果,所以GI ...

  2. iOS 模仿QQ音乐用Xcode写的一个播放器

    ** 我使用AvPlayer写的,所以大家一定得记住要导入AvFoundation的库,话不多说,直接上代码 ** 这里我是把AvPlayer封装到了一个类里, // // CXPlayMvViewC ...

  3. WMPlayer视频播放器,AVPlayer的封装

    WMPlayer视频播放器,AVPlayer的封装,继承UIView,想怎么玩就怎么玩.支持播放mp4.m3u8.3gp.mov,网络和本地视频同时支持.全屏和小屏播放同时支持.自动感应旋转屏幕.ht ...

  4. iOS实现基于VLC播放器的封装效果

    前言: 在一些特定场景下,我们获取到的音视频,由于格式比较特殊,用avplayer等播放器是无法播放的,此时,我们可以借助强大的VLC播放器来处理. 原理这里不再赘述,下面我们讲一下如何添加VLC播放 ...

  5. linux p2p视频播放器,avplayer: 一个基于FFmpeg、libtorrent的P2P播放器实现.

    一直以来, 在多媒体播放器这块, 即使目前有许多开源的播放器项目, 但要写一个播放器仍然是件非常困难的事, 如果在windows上你有可能需要熟悉DShow, 另外的话, 你需要学习一堆开源项目(比如 ...

  6. iOS AVPlayer视频播放器

    代码地址如下: http://www.demodashi.com/demo/11168.html 一.运行效果 二.实现过程 ①.创建播放器avPlayer //创建播放器url = [url str ...

  7. 基于 AVPlayer 自定义播放器

    如果我只是简单的播放一个视频,而不需要考虑播放器的界面.iOS9.0 之前使用 MPMoviePlayerController, 或者内部自带一个 view 的 MPMoviePlayerViewCo ...

  8. Android播放器基础封装库PlayerBase

    简述 视频播放是我们开发中比较常见的场景.这两年关于视频方面的热度不断提升,可以说前两年是直播年,今年是小视频年,各种短视频应用铺天盖地.对于视频的业务场景也越来越丰富,功能也越来越多.对于我们开发来 ...

  9. iOS 视频播放(AVPlayer)

    文章目录一.AVPlayer1.简介2.视频播放的解决方案二.AVPlayer中的MVC1.v层 :AVPlayerLayer2.m层 :AVPlayerItem3.c层 :AVPlayer三.实现简 ...

最新文章

  1. MyRocks: 为facebool 的社交图谱服务的LSM-tree存储引擎
  2. 这本 Kubernetes 图书,你一定不要错过!
  3. 总结自己的Git常用命令
  4. C#指针使用学习总结
  5. 【英语学习】【Level 07】U07 Stories of my Life L5 I shouldn't have said that
  6. (转)Java DecimalFormat 用法(数字格式化)
  7. pycharm修改选中字体颜色
  8. 离线数据系统之oozie详解
  9. 煤矿AI智能视频分析识别系统解决方案
  10. 计算机毕业设计Java-ssm博雅楼自习室预约系统源码+系统+数据库+lw文档
  11. C语言 :探究Char 到底是啥
  12. Vue之导出xlsx
  13. 麦卡锡问答:什么是人工智能?
  14. Caused by: java.lang.ClassNotFoundException: org.springframework.scheduling.quartz.CronTriggerBean
  15. Tcl列表元素查找命令lsearch
  16. 从零搭建Spring Boot脚手架:开篇以及技术选型1
  17. 程序员追星如此硬核?杨超越杯编程大赛获奖项目大盘点!
  18. 破解路由器基于MAC地址过滤策略的方法
  19. 无胁科技-TVD每日漏洞情报-2022-11-10
  20. Linux运维之道之ENGINEER1.0(系统安全,高级连接,防火墙策略)

热门文章

  1. NFV、DPDK以及部分用户态协议研究
  2. 手机cpu厂商和手机cpu架构
  3. Unity结合HTC Vive开发之控制UI始终在视野前方
  4. IIS7 实现301永久重定向
  5. sql查询近一年的资金总和
  6. 多语言请求的API接口获取1688商品详情信息
  7. 微信手机app支付java
  8. 攻防世界 Misc高手进阶区 2分题 打野
  9. python字符串逆向垂直输出型烟雾机_第二章 灯光基础知识
  10. 66.plus-one