Вернуть zip-файл JSZip в React

У меня есть функция, которая заархивирует файл, но она возвращает обещание. Я пытался реализовать это: jszip stackoverflow, но он все еще возвращает обещание , нерешенный.

Я не очень знаком с промисами и совсем не знаком с jsZIP - как мне заставить эту функцию возвращать заархивированный файл?

const getZip = (url) => {
        setLoadingStatus('zipping...')
        let zip = new JSZip();
        console.log("TYPOF URL", url, typeof url)
        zip.file("Report", url)
        var promise = null;
        if (JSZip.support.uint8array) {
        promise = zip.generateAsync({type : "uint8array"}).then(content => {
            saveAs(content, "Report.zip");
          });
        } else {
        promise = zip.generateAsync({type : "string"}).then(content => {
            saveAs(content, "Report.zip");
          });
        }
        console.log("TYPEOF PROMISE", promise, typeof promise) //typeof is always promise
        return promise
    }

С асинхронным ожиданием:

    async function getZip(url){
    setLoadingStatus('zipping...')
    let zip = new JSZip();
    console.log("TYPOF URL", url, typeof url)
    zip.file("Report", url)
    var content = null;
    if (JSZip.support.uint8array) {
        content = await zip.generateAsync({type : "uint8array"}).then(content => {
            saveAs(content, "Report.zip");
          })
    } else {
        content = await zip.generateAsync({type : "string"}).then(content => {
            saveAs(content, "Report.zip");
          })
    }
    return content
}

Функция, которая вызывает getZip():

const createURL = useCallback((node, reportHeight, reportWidth, imgHeight) => {

        domtoimage.toJpeg(node, {width: reportInViewerRef.current.offsetWidth, height: reportInViewerRef.current.scrollHeight})
    .then(function (dataUrl) {
        const pdf = new jsPDF('p', 'px', [reportWidth, imgHeight]);
        setLoadingStatus('pdf created')
        pdf.addImage(dataUrl, 'JPEG', 0, -12, reportWidth, reportHeight );
        setLoadingStatus('image added')
        let fileName = getUniqueFilename()
        let base64pdf = getZip(btoa(fileName, pdf.output()));
        console.log("ZIP FILE =================", base64pdf)
        let storageRef = firebase.storage().ref();
        setLoadingStatus('uploading...')
        let fileLocation = '/pdfs/' + fileName + '.zip'
        let reportPdfRef = storageRef.child(fileLocation);
        reportPdfRef.putString(base64pdf, 'base64').then(function() {
            console.log('Uploaded a data_url string!', reportPdfRef,
            reportPdfRef.getDownloadURL().then(function(url) {
                setLoadingStatus('linking...')
                setTemplateParams({
                    to: sessionData.customerEmail,
                    customers: name, 
                    adviser: adviserFullName,
                    adviserEmail: adviserEmail,
                    adviserAvatar: adviserAvatar,
                    content: url

                }) 
                firebase
                .firestore()
                .collection('sessions')
                .doc(sessionData.id)
                .update({
                    'pdfDownloadURL': url
                }).then(function() {
                    setLoadingStatus('sending email')
                    setSendingEmail(false);
                  });
              }).catch(function(error) {
                switch (error.code) {
                  case 'storage/object-not-found':
                        console.log("FILE DOES NOT EXIST")
                    break;
                  case 'storage/unauthorized':
                        console.log("USER DOES NOT HAVE PERMISSION TO ACCESS THIS FILE")
                    break;

                  case 'storage/canceled':
                        console.log("USER CANCELLED THE UPLOAD")
                    break;
                  case 'storage/unknown':
                        console.log("SERVER ERROR")
                    break;
                }
              })
            )
          });
        })
        .catch(function (error) {
            console.error('oops, something went wrong!', error);
        });
}, [])

person Davtho1983    schedule 06.05.2020    source источник
comment
Проще всего было бы просто использовать async/await. Поэтому сделайте функцию асинхронной, а затем сделайте что-то вроде let content= await zip.generateAsync(...); saveAs(content, "Report.zip");, и тогда вы сможете вернуть содержимое, если оно понадобится где-то еще.   -  person Jayce444    schedule 06.05.2020
comment
Можете ли вы конкретизировать это на примере? Извините, я просто не знаком с асинхронным ожиданием   -  person Davtho1983    schedule 06.05.2020
comment
Почему вы отметили вопрос reactjs? Я не вижу ничего, связанного с реакцией. Если вы собираетесь использовать это внутри реагирующего компонента и не знаете, как это сделать, добавьте соответствующий код. В противном случае просто удалите тег. Поскольку вы, кажется, называете setLoadingStatus, я думаю, эта функция определена внутри компонента с использованием useState?   -  person trixn    schedule 06.05.2020
comment
Это буквально пример, ха-ха, это то, что вам нужно. Итак, в определении функции вы ставите async, например: const getZip = async (url)..., а затем используете этот код в моем первом комментарии. Содержимое, которое у вас было в .then, - это то, что превращается в возвращаемое значение ожидаемого вызова, поэтому сейчас нет необходимости в переменной promise, просто переименуйте ее в content.   -  person Jayce444    schedule 06.05.2020
comment
Куда вы звоните getZip? Пожалуйста, также покажите окружающий компонент.   -  person trixn    schedule 06.05.2020
comment
Хм, Jaycee444, который все еще возвращает обещание, не дожидаясь завершения, поставит новый код под сомнение   -  person Davtho1983    schedule 06.05.2020
comment
@ Davtho1983 Нам нужно знать, почему вы хотите вернуть почтовый индекс. Поэтому, пожалуйста, также поделитесь кодом, который использует getZip. Поскольку создание zip является асинхронным, вы не можете просто использовать его синхронно. Ваш код, использующий getZip, также должен быть асинхронным. Поскольку вы отметили вопрос reactjs, также покажите компонент. Возможно, вам даже не нужно будет возвращать почтовый индекс, но используйте совершенно другой подход.   -  person trixn    schedule 06.05.2020
comment
Хорошо, я добавил функцию, которая вызывает getZip() - мне нужно загрузить zip в firebase   -  person Davtho1983    schedule 06.05.2020


Ответы (1)


Поскольку создание zip является асинхронным, обработка кода также должна быть асинхронной. Сделайте свой createURL обработчик асинхронным и await zip внутри него:

const createURL = useCallback(async (node, reportHeight, reportWidth, imgHeight) => {
    try {
        const dataUrl = await domtoimage.toJpeg(node, {width: reportInViewerRef.current.offsetWidth, height: reportInViewerRef.current.scrollHeight})
    } catch (error) {
        return console.error('oops, something went wrong!', error);
    }

    // ... you can use dataUrl from here

    let base64pdf = await getZip(btoa(fileName, pdf.output()));

    // ... do something with the zip file
}

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

person trixn    schedule 06.05.2020
comment
Это определенно имеет смысл - это не позволяет мне сделать мой useCallback асинхронным, хотя? - person Davtho1983; 06.05.2020
comment
@ Davtho1983 Где вы разместили ключевое слово async? Он должен быть непосредственно перед вашей стрелочной функцией, не перед useCallback. Это определенно работает. Что вы имеете в виду под это не позволяет мне? Есть ли ошибка? Вызывается ли обработчик createURL как обработчик событий? - person trixn; 06.05.2020
comment
У меня есть const createURL = useCallback(async (node, reportHeight, reportWidth, imgHeight) => {... let base64pdf = await getZip(btoa(fileName, pdf.output()));... ошибка, которую я получаю, это анализ ошибка: нельзя использовать ключевое слово «ожидание» вне асинхронной функции - person Davtho1983; 06.05.2020
comment
Это потому, что он внутри тогда? - person Davtho1983; 06.05.2020
comment
@ Davtho1983 Да, это может быть причиной. Вы также можете использовать async/await там. Позвольте мне обновить мой ответ. - person trixn; 06.05.2020
comment
@ Davtho1983 Davtho1983 Чтобы сохранить согласованность, вы также можете использовать async/await для toJpeg. В противном случае вам также придется сделать свой обработчик .then() асинхронным. - person trixn; 06.05.2020