|
47 | 47 | var Vue = require('./vue.min'); // 某些版本不兼容 require('vue');
|
48 | 48 | var StringUtil = require('../apijson/StringUtil');
|
49 | 49 | var CodeUtil = require('../apijson/CodeUtil');
|
| 50 | + var FileUtil = require('../apijson/FileUtil'); |
50 | 51 | var JSONObject = require('../apijson/JSONObject');
|
51 | 52 | var JSONResponse = require('../apijson/JSONResponse');
|
52 | 53 | var JSONRequest = require('../apijson/JSONRequest');
|
@@ -6784,97 +6785,164 @@ https://github.com/Tencent/APIJSON/issues
|
6784 | 6785 | const doc = (this.currentRemoteItem || {}).Document || {}
|
6785 | 6786 | const random = cri.Random || {}
|
6786 | 6787 | const ind = isSub && this.currentRandomSubIndex != null ? this.currentRandomSubIndex : this.currentRandomIndex;
|
| 6788 | + const index = ind != null && ind >= 0 ? ind : -1; // items.length; |
| 6789 | + const server = this.server |
6787 | 6790 |
|
6788 | 6791 | var selectedFiles = Array.from(event.target.files);
|
6789 | 6792 | // const previewList = document.getElementById('previewList');
|
6790 | 6793 | // previewList.innerHTML = '';
|
6791 | 6794 |
|
6792 | 6795 | selectedFiles.forEach((file, i) => {
|
6793 | 6796 | const reader = new FileReader();
|
6794 |
| - reader.onload = (e) => { |
| 6797 | + reader.onload = (evt) => { |
6795 | 6798 | // const img = document.createElement('img');
|
6796 |
| - // img.src = e.target.result; |
| 6799 | + // img.src = evt.target.result; |
6797 | 6800 | // img.style.height = '100%';
|
6798 | 6801 | // img.style.margin = '1px';
|
6799 | 6802 | // previewList.appendChild(img);
|
6800 | 6803 |
|
6801 |
| - const index = ind != null && ind >= 0 ? ind : -1; // items.length; |
6802 |
| - var item = JSONResponse.deepMerge({ |
6803 |
| - Random: { |
6804 |
| - id: -(index || 0) - 1, //表示未上传 |
6805 |
| - toId: random.id, |
6806 |
| - userId: random.userId || doc.userId, |
6807 |
| - documentId: random.documentId || doc.id, |
6808 |
| - count: 1, |
6809 |
| - name: '分析位于 ' + index + ' 的这张图片', |
6810 |
| - img: e.target.result, |
6811 |
| - config: '' |
| 6804 | + function callback(file, img, name, size, width, height, index, rank) { |
| 6805 | + |
| 6806 | + var item = JSONResponse.deepMerge({ |
| 6807 | + Random: { |
| 6808 | + id: -(index || 0) - 1, //表示未上传 |
| 6809 | + toId: random.id, |
| 6810 | + userId: random.userId || doc.userId, |
| 6811 | + documentId: random.documentId || doc.id, |
| 6812 | + count: 1, |
| 6813 | + name: '分析位于 ' + index + ' 的这张图片', |
| 6814 | + img: img, |
| 6815 | + config: '' |
| 6816 | + } |
| 6817 | + }, items[index] || {}); |
| 6818 | + item.status = 'uploading'; |
| 6819 | + |
| 6820 | + const r = item.Random || {}; |
| 6821 | + r.name = r.file = name; |
| 6822 | + r.size = size; |
| 6823 | + r.width = width; |
| 6824 | + r.height = height; |
| 6825 | + r.rank = rank; |
| 6826 | + |
| 6827 | + if (index < 0) { // || r.id == null || r.id <= 0) { |
| 6828 | + items.unshift(item); |
| 6829 | + } else { |
| 6830 | + items[index] = item; |
6812 | 6831 | }
|
6813 |
| - }, items[index] || {}); |
6814 |
| - item.status = 'uploading'; |
6815 | 6832 |
|
6816 |
| - const r = item.Random || {}; |
6817 |
| - r.name = r.file = file.name; |
6818 |
| - r.size = file.size; |
6819 |
| - r.width = file.width; |
6820 |
| - r.height = file.height; |
6821 |
| - |
6822 |
| - if (index < 0) { // || r.id == null || r.id <= 0) { |
6823 |
| - items.unshift(item); |
6824 |
| - } else { |
6825 |
| - items[index] = item; |
6826 |
| - } |
| 6833 | + if (isSub) { |
| 6834 | + App.randomSubs = items; |
| 6835 | + } else { |
| 6836 | + App.randoms = items; |
| 6837 | + } |
6827 | 6838 |
|
6828 |
| - if (isSub) { |
6829 |
| - this.randomSubs = items; |
6830 |
| - } |
6831 |
| - else { |
6832 |
| - this.randoms = items; |
6833 |
| - } |
| 6839 | + try { |
| 6840 | + Vue.set(items, index < 0 ? 0 : index, item); |
| 6841 | + } catch (e) { |
| 6842 | + console.error(e) |
| 6843 | + } |
6834 | 6844 |
|
6835 |
| - try { |
6836 |
| - Vue.set(items, index < 0 ? 0 : index, item); |
6837 |
| - } catch (e) { |
6838 |
| - console.error(e) |
6839 |
| - } |
| 6845 | + const formData = new FormData(); |
| 6846 | + formData.append('file', file); |
6840 | 6847 |
|
6841 |
| - const formData = new FormData(); |
6842 |
| - formData.append('file', file); |
| 6848 | + fetch(server + '/upload', { |
| 6849 | + method: 'POST', |
| 6850 | + body: formData |
| 6851 | + }) |
| 6852 | + .then(response => response.json()) |
| 6853 | + .then(data => { |
| 6854 | + var path = data.path; |
| 6855 | + if (StringUtil.isEmpty(path, true) || data.size == null) { |
| 6856 | + throw new Error('上传失败!' + JSON.stringify(data || {})); |
| 6857 | + } |
6843 | 6858 |
|
6844 |
| - fetch(this.server + '/upload', { |
6845 |
| - method: 'POST', |
6846 |
| - body: formData |
6847 |
| - }) |
6848 |
| - .then(response => response.json()) |
6849 |
| - .then(data => { |
6850 |
| - var path = data.path; |
6851 |
| - if (StringUtil.isEmpty(path, true) || data.size == null) { |
6852 |
| - throw new Error('上传失败!' + JSON.stringify(data || {})); |
6853 |
| - } |
| 6859 | + console.log('Upload successful:', data); |
| 6860 | + item.status = 'done'; |
| 6861 | + if (!(server.includes('localhost') || server.includes('127.0.0.1'))) { |
| 6862 | + r.img = (path.startsWith('/') ? server + path : path) || r.img; |
| 6863 | + } |
6854 | 6864 |
|
6855 |
| - console.log('Upload successful:', data); |
6856 |
| - item.status = 'done'; |
6857 |
| - if (! (App.server.includes('localhost') || App.server.includes('127.0.0.1'))) { |
6858 |
| - r.img = (path.startsWith('/') ? App.server + path : path) || r.img; |
6859 |
| - } |
| 6865 | + try { |
| 6866 | + Vue.set(items, index < 0 ? 0 : index, item); |
| 6867 | + } catch (e) { |
| 6868 | + console.error(e) |
| 6869 | + } |
6860 | 6870 |
|
6861 |
| - try { |
6862 |
| - Vue.set(items, index < 0 ? 0 : index, item); |
6863 |
| - } catch (e) { |
6864 |
| - console.error(e) |
6865 |
| - } |
| 6871 | + App.updateRandom(r) |
| 6872 | + }) |
| 6873 | + .catch(error => { |
| 6874 | + console.error('Upload failed:', error); |
| 6875 | + item.status = 'failed'; |
| 6876 | + }); |
| 6877 | + } |
| 6878 | + |
| 6879 | + if (file.type && file.type.startsWith("video/")) { |
| 6880 | + // === 视频处理 === |
| 6881 | + this.extractFramesAndUpload(file, 5, async (frameBlob, rank, totalFrames) => { |
| 6882 | + const reader2 = new FileReader(); |
| 6883 | + reader2.onload = (evt) => { // TODO 传 toId,视频作为分组,次数作为抽取帧数 |
| 6884 | + var fn = file.name + '-' + rank + '.jpg' |
| 6885 | + callback(new File([frameBlob], fn), reader2.result, fn, frameBlob.size, frameBlob.width, frameBlob.height, -1, rank) |
| 6886 | + } |
| 6887 | + reader2.readAsDataURL(frameBlob); |
| 6888 | + }); |
| 6889 | + } else { |
| 6890 | + callback(file, reader.result, file.name, file.size, file.width, file.height, index) |
| 6891 | + } |
6866 | 6892 |
|
6867 |
| - App.updateRandom(r) |
6868 |
| - }) |
6869 |
| - .catch(error => { |
6870 |
| - console.error('Upload failed:', error); |
6871 |
| - item.status = 'failed'; |
6872 |
| - }); |
6873 | 6893 | };
|
6874 | 6894 | reader.readAsDataURL(file);
|
6875 | 6895 | });
|
6876 | 6896 | },
|
6877 | 6897 |
|
| 6898 | + /** |
| 6899 | + * 从视频抽帧(倒序),支持并发上传 |
| 6900 | + * @param {File} file - 视频文件 |
| 6901 | + * @param {number} concurrency - 最大并发数 |
| 6902 | + * @param {function} onFrameReady - 回调(frameBlob, rank, totalFrames) |
| 6903 | + */ |
| 6904 | + extractFramesAndUpload: function(file, concurrency, onFrameReady) { |
| 6905 | + const url = URL.createObjectURL(file); |
| 6906 | + const video = document.createElement("video"); |
| 6907 | + video.src = url; |
| 6908 | + video.preload = "metadata"; |
| 6909 | + |
| 6910 | + video.onloadedmetadata = async () => { |
| 6911 | + const duration = video.duration; |
| 6912 | + let timestamps = []; |
| 6913 | + |
| 6914 | + if (duration <= 100) { |
| 6915 | + for (let t = Math.floor(duration); t >= 0; t--) timestamps.push(t); |
| 6916 | + } else { |
| 6917 | + const step = duration / 100; |
| 6918 | + for (let i = 100; i >= 0; i--) timestamps.push(i * step); |
| 6919 | + } |
| 6920 | + |
| 6921 | + const totalFrames = timestamps.length; |
| 6922 | + let index = 0; |
| 6923 | + |
| 6924 | + async function worker() { |
| 6925 | + while (index < totalFrames) { |
| 6926 | + const i = index++; |
| 6927 | + const time = timestamps[i]; |
| 6928 | + const frameBlob = await FileUtil.captureFrame(video, time); |
| 6929 | + // rank 按倒序:0 表示最新帧,totalFrames-1 表示最早帧 |
| 6930 | + const rank = i; |
| 6931 | + onFrameReady(frameBlob, rank, totalFrames); |
| 6932 | + } |
| 6933 | + } |
| 6934 | + |
| 6935 | + // 并发执行 |
| 6936 | + const workers = []; |
| 6937 | + for (let i = 0; i < concurrency; i++) { |
| 6938 | + workers.push(worker()); |
| 6939 | + } |
| 6940 | + await Promise.all(workers); |
| 6941 | + |
| 6942 | + URL.revokeObjectURL(url); |
| 6943 | + }; |
| 6944 | + }, |
| 6945 | + |
6878 | 6946 | uploadImage: function(randomIndex, randomSubIndex) {
|
6879 | 6947 | const isSub = randomSubIndex != null;
|
6880 | 6948 | const items = (isSub ? this.randomSubs : this.randoms) || [];
|
|
0 commit comments