目录
先使用uni.downloadFile将文件下载到将远程文件下载到小程序内存中,然后使用uni.saveVideoToPhotosAlbum保存到本地系统
注意:使用临时路径,则报错 "saveVideoToPhotosAlbum:fail invalid file type",因此我使用了固定路径,发现可以下载成功
// 下载
async download() {
let that = this
await uni.showLoading({
title: '下载中'
})
let filePath = wx.env.USER_DATA_PATH + '/' + that.word.filename +'.mp4'
//将远程文件下载到小程序内存中
uni.downloadFile({
url: that.word.fileurl,//临时路径
filePath: filePath,//指定路径,去文件管理的微信中查看
success(res) {
// 2 成功下载后而且状态码为200时将视频保存到本地系统
if (res.statusCode === 200) {
uni.saveVideoToPhotosAlbum({
// filePath: res.tempFilePath,
filePath: filePath,
success(res) {
console.log(res, 'success')
},
fail(error) {
console.log(error, 'error')
}
})
uni.hideLoading();
// 提示用户下载成功
uni.showToast({
title: "下载成功",
icon: "success"
});
}
// 如果该资源不可下载或文件格式出错则提示用户
else {
uni.showToast({
title: "资源格式错误,请联系管理员"
});
}
},
fail: (err) => {
// 下载失败提醒
uni.hideLoading();
uni.showToast({
title: "下载失败"
})
}
})
},
小程序的分享不能单独分享视频,只能分享页面。所以我就新建了一个分享的页面,然后把视频用列表的形式呈现。就和百度网盘小程序分享的页面差不多。

点击视频后,全屏播放,里面有下载和分享的操作

视频分享的全局js可以看我的另一篇文章:微信小程序分享
分享的新页面new.vue
<template>
<view class="">
<uni-list v-for="(item,index) in list" :key='index'>
<view :border="none" :padding="0" :spacing="0" style="padding:0" :is-shadow="false" :isFull="true">
<view @click="showvideo(item)" class="card-title" style="display: flex;justify-content: space-between;">
<view style="display: flex;">
<uni-badge class="uni-badge-left-margin" :is-dot="true" :text="item.Isupdate?value:''"
absolute="rightTop" size="normal">
<view>
<image v-if="item.fileExt==='.mp4'" class="slot-image"
src="/static/shipin_lvhangyingxiang.png" mode="widthFix">
</image>
<image v-else-if="item.fileExt==='.pdf'" class="slot-image" src="/static/pdfwenjian.png"
mode="widthFix">
</image>
</view>
</uni-badge>
<view class="title-box">
<view class="">{{item.filename}}
</view>
<view class="">{{item.AddTime}}</view>
</view>
</view>
</view>
</view>
</uni-list>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
};
},
onLoad(e) {
let obj = e.item.replace("\"([^\"]*)\"", "$1");
this.list = JSON.parse(obj)
},
methods: {}
}
</script>
<style>
</style>
点击视频后全屏播放的页面appUpdate.vue
<template>
<view class="appUpdateMask">
<view class="appUpdateContent">
<video autoplay controls object-fit="cover" id="myvideo" :src="videoUrl"
@fullscreenchange="screenChange"></video>
<view class="btns">
<button class="btn" @click="download()">
<text>转存到我的手机</text>
</button>
<button class="btn" open-type="share" :data-obj="obj" style="background-color: rgba(102, 102, 102);">
<text>转发给朋友</text>
</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
videoUrl: '',
videoPlay: false,
videoContext: '',
obj: []
}
},
onLoad(e) {
this.obj.push(JSON.parse(e.item))
let a = JSON.parse(e.item)
this.videoContext = uni.createVideoContext("myvideo", this); // this这个是实例对象 必传
this.videoUrl = a.fileurl;
this.videoContext.requestFullScreen({
direction: 0
});
this.videoContext.play();
},
methods: {
screenChange(e) {
let fullScreen = e.detail.fullScreen; // 值true为进入全屏,false为退出全屏
// console.log(e, "全屏");
if (!fullScreen) {
//退出全屏
this.videoPlay = false; // 隐藏播放盒子
}
},
// 下载
async download() {
let that = this
wx.getSavedFileList({ // 获取文件列表
success(res) {
// 文件列表超过20个文件,则删除前10个
if (res.fileList.length > 20) {
res.fileList.forEach((val, key) => { // 遍历文件列表里的数据
if (key < 10) {
// 删除存储的垃圾数据
wx.removeSavedFile({
filePath: val.filePath
});
}
})
}
}
})
await uni.showLoading({
title: '下载中'
})
let filePath = wx.env.USER_DATA_PATH + '/' + that.obj[0].filename + '.mp4'
//将远程文件下载到小程序内存中
uni.downloadFile({
url: that.obj[0].fileurl,
// filePath: filePath,
success(res) {
console.log(res)
// 2 成功下载后而且状态码为200时将视频保存到本地系统
if (res.statusCode === 200) {
uni.saveVideoToPhotosAlbum({
// filePath: res.tempFilePath,
filePath: filePath,
success(res) {
// console.log(res, 'success')
uni.hideLoading();
uni.showToast({
title: "下载成功",
icon: "success"
});
// 保存下载记录
this.api.DownloadFile({
FileId: that.obj[0].Id
}).then(res => {
// console.log(res)
})
},
fail(error) {
console.log(error, 'error')
uni.hideLoading();
uni.showToast({
title: "下载失败",
icon: "success"
});
}
})
} else {
// 如果该资源不可下载或文件格式出错则提示用户
uni.showToast({
title: "资源格式错误,请联系管理员"
});
}
},
fail: (err) => {
// 下载失败提醒
uni.hideLoading();
uni.showToast({
title: "下载失败"
})
}
})
},
}
}
</script>
<style lang="scss">
page {
background: transparent;
}
video {
width: 100%;
}
.appUpdateMask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 1);
}
.appUpdateContent {
width: 100vw;
}
.btn {
display: flex;
// flex-direction: row;
align-items: center;
justify-content: space-between;
line-height: 20rpx;
background-color: #1480cd;
border-radius: 50rpx;
text-align: center !important;
padding: 30rpx 60rpx;
width: 45%;
color: #fff;
image {
width: 35rpx;
height: 35rpx;
}
text {
width: 100%;
display: block;
height: 40rpx;
line-height: 40rpx;
font-size: 26rpx
}
}
.btns {
display: flex;
justify-content: space-around;
position: relative;
top: 200rpx;
}
</style>
功能点:点击文件进行下载好分享
问题:发现并不能点击列表后直接将文件下载到手机上,而是需要先进行临时下载,下载完成后自动打开文档预览,然后需要用户点击右上角的三点,选择保存到手机还是转发等相应操作。

async download() {
await uni.showLoading({
title: '下载中'
})
uni.downloadFile({
url: this.word.url,
success(res) {
wx.getFileSystemManager().saveFile({
tempFilePath: res.tempFilePath,
// filePath: wx.env.USER_DATA_PATH + '/' + '上传成员.pdf',//自定义文件名
success(res) {
console.log(res)
wx.openDocument({
filePath: res.savedFilePath,
showMenu: true,
success: function(res) {
uni.hideLoading()
uni.showToast({
title: '自动打开文件',
icon: 'none'
})
},
fail(error) {}
})
uni.hideLoading()
uni.showToast({
title: '下载文件成功',
icon: 'none'
})
}
})
},
fail(error) {}
})
}
},
<script>
export default{
data(){
return{
loadProgress: 0,//加载index
CustomBar: this.CustomBar,
dltDownLvNew:"",//已下载
dltDownLvAll:"",//总长度
dltDownLvWc:"",//完成率
downloadUlr:"",//下载地址
suffix:"",//文件后缀
}
},
methods:{
clickPeople(e){//点击下载
let _this = this;
//下载地址
this.downloadUlr = e;
//获取地址后缀
this.suffix = e.split(".")[e.split(".").length - 1];
//判断是否为(图片或视频)
if(e.substring(e.length - 3) == "MP4" || e.substring(e.length - 3) == "mp4" || e.substring(e.length - 3) == "jpg" || e.substring(e.length - 3) == "png"){
const downloadTask = uni.downloadFile({
url:e,
success: res => {
if (res.statusCode === 200) {
if(res.tempFilePath.substring(res.tempFilePath.length - 3) == "mp4" || res.tempFilePath.substring(res.tempFilePath.length - 3) == "MP4"){//视频
//保存视频到相册
uni.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success: function () {
uni.showToast({
title: '保存成功',
icon: 'none',
duration:2000,
mask:true
});
},
fail: function() {
this.loadProgress = 0;
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
}else{//图片
// 图片保存到手机相册
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function() {
uni.showToast({
title: '保存成功',
icon: 'none',
duration:2000,
mask:true
});
},
fail: function() {
this.loadProgress = 0;
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
}
}else{
uni.showToast({
title:'下载失败',
icon:"none"
})
}
},
fail:(err) => {
this.loadProgress = 0;
uni.showToast({
icon:"none",
mask:true,
title:'下载失败'
})
}
});
downloadTask.onProgressUpdate((res) => {
this.loadProgress = res.progress;
if (this.loadProgress < 100) {
} else {
this.loadProgress = 0;
}
if(res.totalBytesExpectedToWrite < 10485760){
this.dltDownLvNew = Math.ceil(res.totalBytesWritten / 1024) + "KB";
this.dltDownLvAll = Math.ceil(res.totalBytesExpectedToWrite / 1024) + "KB";
this.dltDownLvWc = res.progress + "%"
}else{
this.dltDownLvNew = Math.ceil(res.totalBytesWritten / 1048576) + "M"
this.dltDownLvAll = Math.ceil(res.totalBytesExpectedToWrite / 1048576) + "M";
this.dltDownLvWc = res.progress + "%"
}
});
}else{
//下载文件为非图片和视频的文件
let _this = this;
const downloadTaskt = uni.downloadFile({
url:e,//下载地址接口返回
success:(data) => {
uni.hideLoading()
if(data.statusCode === 200){
var sFilePath = data.tempFilePath
var sFileName = _this.downloadUlr.split('/')[_this.downloadUlr.split('/').length - 1];//原来的文件名
//#ifdef APP-PLUS
//文件保存到本地
uni.saveFile({
tempFilePath: data.tempFilePath,//临时路径
success:function(res){
var savedFilePath = res.savedFilePath;
let osname = plus.os.name;
//ios手机直接打开文件,手动存储文件到手机,Android手机从根目录创建文件夹,保存文件并改名
if (osname == 'Android') {
uni.showToast({
icon:'none',
mask:true,
title:'保存成功',
duration:1000,
});
_this.fSetFileName(res.savedFilePath, sFilePath);
}
setTimeout(() => {
//打开文档查看
uni.openDocument({
filePath:res.savedFilePath,
success:function(res){
// console.log("成功打开文件")
},
fail(){
// console.log("打开文件失败")
}
})
},1000)
},
fail:function(res){
}
});
//#endif
//#ifdef MP-WEIXIN
//小程序对文件下载并不友好,不建议使用小程序当做下载器
const FileSystemManager = wx.getFileSystemManager();
FileSystemManager.saveFile({//下载成功后保存到本地
tempFilePath:data.tempFilePath,
filePath:wx.env.USER_DATA_PATH + "/" + sFileName,
success(res2){
if(res2.errMsg == 'saveFile:ok'){
// 判断手机平台是 Android 还是 IOS
if (uni.getSystemInfoSync().platform === 'android') {
// console.log('运行Android上')
uni.showModal({
title:"保存地址为",
content:"手机存储/Android/data/com.tencent.mm/MicroMsg/wxanewfiles"
})
} else {
// console.log('运行iOS上')
uni.showToast({
title:"请转移APP下载",
icon:"none"
})
}
}else{
uni.showToast({
title:"下载失败",
icon:"none"
})
}
},
fail(){
uni.showToast({
title:"下载失败",
icon:"none"
})
}
})
//#endif
}else{
this.loadProgress = 0;
uni.showToast({
icon:"none",
mask:true,
title:"下载失败"
})
}
},
fail:(err) => {
this.arcZzShow = false;
this.loadProgress = 0;
uni.showToast({
icon:"none",
mask:true,
title:"下载失败"
})
}
})
downloadTaskt.onProgressUpdate((res) => {
this.loadProgress = res.progress;
if (this.loadProgress < 100) {
} else {
this.loadProgress = 0;
}
if(res.totalBytesExpectedToWrite < 10485760){
this.dltDownLvNew = Math.ceil(res.totalBytesWritten / 1024) + "KB";
this.dltDownLvAll = Math.ceil(res.totalBytesExpectedToWrite / 1024) + "KB";
this.dltDownLvWc = res.progress + "%"
}else{
this.dltDownLvNew = Math.ceil(res.totalBytesWritten / 1048576) + "M"
this.dltDownLvAll = Math.ceil(res.totalBytesExpectedToWrite / 1048576) + "M";
this.dltDownLvWc = res.progress + "%"
}
});
}
},//点击下载END
// 给下载的文件重命名
fSetFileName(sFilePath, sFileName) {
var sFileName = sFileName.split('/')[sFileName.split('/').length - 1];//原来的文件名
var aFileUrl = sFilePath.split('/').splice(0, sFilePath.split('/').length - 1);//saveFile API保存的本地地址
var url = this.downloadUlr;//下载地址
// 'url下载地址(和上面的一样)'
let dtask = plus.downloader.createDownload(url, {
filename: "file://storage/emulated/0/AAA/" + sFileName //在手机存储更目录创建一个叫AAA的文件夹,把文件存储进去,并更改会原名
},
(d, status) => {
if (status == 200) {
let fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);
} else {
//下载失败
plus.downloader.clear(); //清除下载任务
}
})
dtask.start();
}
}
}
</script>
1、有时候手机和平板必须有wps才可以打开
2、打开文件可能存在跨域,这个需要后台去进行配置cors
3、使用uni.downloadFile()的文件路径(url)必须是浏览器能直接访问的文件。比如:http://xx.cc.com/images/abc.pdf这种格式。 否则调用 uni.openDocument 是失败的,原因可能是无法识别文件类型
4、
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A
我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只