Как передать данные метеорному методу? - PullRequest
0 голосов
/ 19 февраля 2019

Я работаю над способом загрузки в ведро AWS S3 с метеорного сервера и реагирования на внешний интерфейс.

Я определил следующие файлы

server/methods.js

import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';

const AWS = require('aws-sdk')
const s3_bucket = "bucket-name"

import { mediaFiles } from '../imports/api/files.collection';

const s3  = new AWS.S3({
    accessKeyId: '<key>',
    secretAccessKey: '<secret>',
    endpoint: 's3.eu-west-2.amazonaws.com',
    region: 'eu-west-2',
    signatureVersion: 'v4'
});

Meteor.methods({
    'aws.getUploadId' (filename, filetype) {
        let params = {
            Bucket: s3_bucket,
            Key: filename,
            ContentType: filetype
        }
        return new Promise((resolve, reject) => {
            s3.createMultipartUpload(params, (err, data) => {
                if (err) reject(err)
                if (data) resolve(data.UploadId)
            })
        })
    },

    'aws.uploadPart' (filename, blob, upload_id, index) {
        let params = {
            Bucket: s3_bucket,
            Key: filename,
            PartNumber: index,
            UploadId: upload_id,
        }

        return new Promise((resolve, reject) => {
            s3.uploadPart(params, (err, data) => {
                if (err) reject(err)
                if (data) resolve(data)
            })
        })
    },

    'aws.completeUpload' (filename, upload_id, upload_parts) {
        console.log("aws.completeUpload called")
        console.log(`filename: ${filename}\nID: ${upload_id}\nUpload_parts****${upload_parts}****`)
        let params = {
            Bucket: s3_bucket,
            Key: filename,
            UploadId: upload_id,
            MultipartUpload: {Parts: upload_parts}
        }

        return new Promise((resolve, reject) => {
            s3.completeMultipartUpload(params, (err, data) => {
                if (err) reject(err)
                if (data) resolve(data)
            })
        })
    },
});

upload.js # client side

import { Meteor } from 'meteor/meteor';
import React, { Component } from 'react';
import { Page, Icon, ProgressBar, Input, Select } from 'react-onsenui';
import _ from 'underscore';

import Navbar from './Navbar';

class Upload extends Component {

    state = { 
        uploadId : '',
        media_file : null,
        filename : '' 
    }

    setUploadFileParameters = (e) => {
        e.preventDefault();
        e.stopPropagation();
        console.log('setUploadFileParameters called')
        const media_file = e.target.files[0]
        const filename = media_file.name
        const filetype = media_file.type

        Meteor.call('aws.getUploadId', filename, filetype, (err, res) => {
            if (err) console.log("Error getting id: ", err)
            if (res) {
                this.setState({ media_file: media_file, filename: filename, uploadId: res })
            }
        })
    }

    uploadIt = (e) => {
        e.preventDefault();
        const t = e.target
        const upload_id = this.state.uploadId
        const media_file = t.media_file.files[0]
        console.log(`mediafile: ${media_file}`)

        try {
            const FILE_CHUNK_SIZE = 10000000 // 10MB
            const fileSize = media_file.size
            const filename = media_file.name
            const NUM_CHUNKS = Math.round(fileSize / FILE_CHUNK_SIZE) + 1
            let start, end, blob
            let upload_parts = []

            for (let index = 1; index < NUM_CHUNKS + 1; index++) {
                start = (index - 1)*FILE_CHUNK_SIZE
                end = (index)*FILE_CHUNK_SIZE
                blob = (index < NUM_CHUNKS) ? media_file.slice(start, end) : media_file.slice(start)

                // Puts each file part into the storage server
                Meteor.call('aws.uploadPart', filename, blob, upload_id, index, (err, res) => {
                    if (err) console.log("uploading part error ", err)
                    if (res) {
                        // console.log("RES: ", typeof res, res)
                        upload_parts.push({Etag: res.ETag, PartNumber: index})    
                    }
                })
            }

            // Generate the parts list to complete the upload
            // Calls the CompleteMultipartUpload endpoint in the backend server
            console.log("upload_parts: ", upload_parts)

            Meteor.call('aws.completeUpload', filename, upload_id, upload_parts, (err, res) => {
                console.log("Complete upload called *****")
                if (err) console.log("Complete upload err: ", err)
                if (res) console.log("Complete upload res: ", res)
            })
        }
        catch(err) {
            console.log(err)
        }
    }

    render() {
        const { showMenu } = this.props
        console.log("State: ", JSON.stringify(this.state))

            return (
                <Page renderToolbar={Navbar('Upload', showMenu)}>
                    <div className="form-container">

                    {Meteor.user() &&
                        <form onSubmit={(e) => this.uploadIt(e)}>

                            <p>File</p>

                            <Input
                                type="file"
                                id="fileinput"
                                ref="fileinput"
                                name="media_file"
                                onChange={e => this.setUploadFileParameters(e)}
                            />
                            <br/>

                            <button
                                type="submit"
                                value="Upload"
                                className="btn upload-work-button" 
                            >
                                Upload
                            </button>
                        </form>
                    }
                    </div>
                </Page>
            )
        }
    }
export default Upload;

У меня проблема в том, что содержимое upload_parts не передается на внутренний сервер метеора.Журнал консоли на внутреннем сервере ничего не возвращает.Он даже не возвращает undefined.Мне нужна помощь с этим.

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Спасибо @Nathan Schwarz за ваш ответ.Вы действительно помогли мне в этом.Вот как я наконец решил свою головоломку.

const uploadParts = (filename, blob, upload_id, index) => {
    return new Promise(
        (resolve, reject) => 
        Meteor.call('aws.uploadPart', filename, blob, upload_id, index, (err, res) => {
            resolve(res)
        })
    )
}

for (let index = 1; index < NUM_CHUNKS; index++) {
    start = (index - 1)*FILE_CHUNK_SIZE
    end = (index)*FILE_CHUNK_SIZE
    blob = (index < NUM_CHUNKS) ? media_file.slice(start, end) : media_file.slice(start)

    const b = new Blob([blob], {type:filetype})
    const c = {size: blob.size, type:filetype}
    console.log("Media ", media_file.size, media_file)
    console.log("Blob: ", blob.size, blob)
    console.log("B: ", b.size, b)

    promises.push(uploadParts(filename, c, upload_id, index))
}

Promise.all(promises).then(res => {
    res.forEach((r, index) => 
        upload_parts.push({ETag: r.ETag, PartNumber: index+1})
    )
    console.log("upload_parts: ", upload_parts)
    Meteor.call('aws.completeUpload', filename, upload_id, upload_parts, (err, res) => {
        if (err) console.log("Complete upload err: ", err)
        if (res) console.log("Complete upload res: ", res)
    })
})

С этим кодом upload_parts завершается раньше Meteor.call('aws.completeUpload',...)

Обратите внимание, как я выполнил обещания в uploadParts.Это следует из того, как работает внутренний сервер.Я могу получить возвращаемое значение только из бэкэнда, если передам обратный вызов Meteor.call().Сам бэкэнд возвращает обещание, как показано ниже.

'aws.uploadPart' (filename, blob, upload_id, index) {
    console.log("aws.uploadPart method")

    console.log("file blob: ", blob)
    console.log("filename: ", filename)

    let params = {
        Bucket: s3_bucket,
        Key: filename,
        PartNumber: index,
        UploadId: upload_id,
        Body: blob,
    }

    return new Promise((resolve, reject) => {
        s3.uploadPart(params, (err, data) => {
            if (err) reject(err)
            if (data) {
                console.log("Upload part return ", data)
                resolve(data)
            }
        })
    })
0 голосов
/ 19 февраля 2019

Ваш массив пуст, потому что вы вызываете асинхронные функции для заполнения массива, поэтому вы должны удалить его при отправке на сервер.

вам нужно заполнить массивы синхронно или заключить их в обещания.

    ....
    const uploadParts = (filename, blob, upload_id, index) => {
       return new Promise((resolve, reject) => resolve(
       Meteor.call('aws.uploadPart', filename, blob, upload_id, index, (err, res) => {
           upload_parts.push({Etag: res.ETag, partNumber: res.index})
       }))
    }
    let promises = []
    for (let index = 1; index < NUM_CHUNKS + 1; index++) {
        start = (index - 1)*FILE_CHUNK_SIZE
        end = (index)*FILE_CHUNK_SIZE
        blob = (index < NUM_CHUNKS) ? media_file.slice(start, end) : 
        media_file.slice(start)
        promises.push(uploadParts(filename, blob, upload_id, index))
    }
    Promise.all(promises).then(() => {
       Meteor.call('aws.completeUpload', filename, upload_id, upload_parts, (err, res) => {
          console.log("Complete upload called *****")
          if (err) console.log("Complete upload err: ", err)
          if (res) console.log("Complete upload res: ", res)
       })
    })

проверьте документацию здесь ('asyncCallback'): https://docs.meteor.com/api/methods.html#Meteor-call

...