1. 文件下载分享保存相册

    • 用户点击页面时,应用会尝试下载或分享指定的文件。
    • 如果设备支持分享(如移动设备上的 navigator.share API),文件将被直接分享。
    • 否则,文件将通过下载链接下载到用户设备。
  2. 用户通知

    • 应用通过弹出通知向用户提供下载或分享的状态更新。

实现细节

  1. HTML 和 CSS

    • 页面结构简洁,以全屏背景图片和中央对齐的内容为主。
    • 通知弹窗使用 CSS 控制显示与隐藏,提供平滑的过渡效果。
  2. Vue.js 应用

    • 使用 Vue.js 定义了一个简单的组件 App,包含数据属性和方法来处理文件操作。
  3. JavaScript 逻辑

    • handleDownload() 方法根据用户设备类型决定下载或分享逻辑。
    • fetchDownloadInfo() 模拟异步请求,从 URL 参数中提取文件链接。
    • showNotification() 方法用于在页面中央显示操作反馈。

交互流程

  • 用户访问页面后,点击任意位置即会触发文件下载或分享。
  • 根据设备类型和功能支持,选择最佳方式(下载或分享)进行文件操作。
  • 操作的结果通过视觉通知反馈给用户,提升用户体验
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
        <title>文件下载和分享</title>
        <style>
            body {
                margin: 0;
                padding: 0;
                background-image: url('https://img2.baidu.com/it/u=1819336312,2385547726&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1722445200&t=d2011eb7dab4b9fdb3e6e8dc425acb0c');
                background-size: cover;
                background-position: center;
                font-family: Arial, sans-serif;
                height: 100vh;
                display: flex;
                justify-content: center;
                align-items: center;
                cursor: pointer;
                overflow: hidden;
            }
            .notification {
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background-color: rgba(0, 0, 0, 0.8);
                color: white;
                padding: 10px 20px;
                border-radius: 5px;
                font-size: 16px;
                opacity: 0;
                pointer-events: none;
                transition: opacity 0.5s;
                z-index: 10;
            }
            .notification.show {
                opacity: 1;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <!-- Vue 模板将被挂载到这里 -->
        </div>
        <div id="notification" class="notification"></div>
        <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
        <script src="https://cdn.jsdelivr.net/npm/vue-router@3"></script>
        <script>
            // Vue 组件
            const App = {
                data() {
                    return {
                        downloadLink: '',
                    };
                },
                methods: {
                    async handleDownload() {
                        const downloadInfo = await this.fetchDownloadInfo();
                        if (!downloadInfo) {
                            this.showNotification('下载失败');
                            return;
                        }
                        const link = downloadInfo.query.downloadLink;
                        const fileName = link.substring(link.lastIndexOf('/') + 1);
    
                        if (this.isMobile() && navigator.share) {
                            this.showLoading();
                            try {
                                const fileBlob = await (await fetch(link)).blob();
                                this.hideLoading();
                                const shareData = { files: [new File([fileBlob], fileName, { type: fileBlob.type })] };
    
                                if (navigator.canShare(shareData)) {
                                    await navigator.share(shareData);
                                    this.showNotification('下载成功了 (Download success)');
                                } else {
                                    throw new Error('无法分享');
                                }
                            } catch (error) {
                                console.error(error);
                                this.retryDownload();
                            }
                        } else {
                            this.downloadFile(link, fileName);
                        }
                    },
                    downloadFile(url, fileName) {
                        // 直接跳转到下载链接,而不是创建临时元素
                        window.location.href = url;
                        this.showNotification('下载开始了 (Download started)');
                    },
                    showNotification(message) {
                        const notification = document.getElementById('notification');
                        notification.textContent = message;
                        notification.classList.add('show');
                        setTimeout(() => {
                            notification.classList.remove('show');
                        }, 1500);
                    },
                    showLoading() {
                        this.showNotification('下载资源中...');
                    },
                    hideLoading() {
                        this.showNotification('下载资源中...');
                    },
                    retryDownload() {
                        console.log('尝试重新下载');
                        window.location.reload();
                    },
                    isMobile() {
                        return /Mobi|Android/i.test(navigator.userAgent);
                    },
                    fetchDownloadInfo() {
                        // 提取完整的 URL
                        const params = new URLSearchParams(window.location.search);
                        const downloadUrl = params.get('file'); // 获取完整的 URL
    
                        return new Promise((resolve) => {
                            setTimeout(() => {
                                resolve({ query: { downloadLink: downloadUrl } });
                            }, 500); // 减少延迟时间
                        });
                    }
                },
                mounted() {
                    // 添加页面点击事件来触发下载
                    document.body.addEventListener('click', this.handleDownload);
                },
                template: `
                    <div>
                        <p></p>
                    </div>
                `
            };
    
            // 定义路由
            const routes = [];
    
            // 创建路由实例
            const router = new VueRouter({
                mode: 'history',
                routes
            });
    
            // 创建 Vue 实例
            new Vue({
                router,
                render: h => h(App)
            }).$mount('#app');
        </script>
    </body>
    </html>
    
    需要注意navigator.share API只能在https环境直接拉起使用

测试使用地址后面直接拼接参数https://your.com/share.html?file=https://your.com/2029005.mp4

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐