Stworzyłem aplikację węzła, która robi to, że jest to, że jest to, że skraca google i pobiera 15 najlepszych obrazów, a następnie przechowywać go w HDD w folderze, który jest zapytaniem otrzymanym po kompresji. Teraz problemem, że stoję w obliczu odczytania tego folderu za pomocą READDIRSYNC i przechowywania wyników w błędzie, zwraca pustą tablicę, co jest nie tak z kodem.

 request(url, function (error, response, body) {
if (!error) {
var $ = cheerio.load(body);
    var imgNodes = $('#ires td a img');
    // imgNodes is merely an array-like object, sigh.
    // This is purposedly old-school JS because newer stuff doesn't work:
    var urls = [];
    for(let i = 0; i <= 14; i++){
        let imgNode = imgNodes[i];
        urls.push(imgNode.attribs['src']);
    }  
// console.log(urls);

const processCompress = new Promise(resolve => {
    fs.mkdir(path.join(__dirname,'Photos',query), function (error) {
    let j = 0;
     if(!error){
        for(i in urls){
            console.log(i);
            var source = tinify.fromUrl(urls[i]);
            source.toFile(path.join(__dirname,'Photos', query,"optimized_"+ ++j +".jpg"));
        }
    }});
    resolve();
});

const getFiles = new Promise(resolve => {
    fs.readdirSync(path.join(__dirname,'Photos', query)).forEach(function (file) {
        fileName.push(path.join(__dirname,'Photos',query,file));
    });
    resolve();
});

function colourMeBw(){
    for(let k = 0; k < fileName.length; k++){
    Jimp.read(fileName[k], (err, image) => {
        if (err) throw err;
        image.greyscale().write(fileName[k]);
    });
}}
processCompress.then(() => getFiles);
colourMeBw(); 
 } else {
console.log("We’ve encountered an error: " + error);
 }
-1
Akshat Gupta 25 czerwiec 2017, 08:14

3 odpowiedzi

Najlepsza odpowiedź

Istnieje wiele rzeczy nie tak z kodem:

  1. W processCompress(), rozstrzygasz obietnicę przed zakończeniem fs.mkdir().

  2. W getFiles() {} znasz synchroniczną funkcję we / wy w obietnicy. Pierwszym problemem jest to, że w ogóle nie powinieneś używać synchronicznego I / O. To najszybszy sposób na uszkodzenie skalowalności serwera. Następnie, po przełączeniu w wersji async fs.readdir(), musisz odpowiednio rozwiązać obietnicę.

  3. Nie ma możliwości wiedzieć, kiedy jest rzeczywiście zrobione.

  4. Należy iterować tablicę z for(i in urls) z różnych powodów. W ES6 możesz użyć for (url of urls). W ES5 można użyć tradycyjnego for (var i = 0; i < urls.length; i++) {} lub urls.forEach().

  5. Nie masz propagacji błędów. Cały proces byłby zadławy, jeśli gdzieś otrzymasz błąd w środku, ponieważ późniejsze części procesu nadal będą próbować wykonać swoją pracę, chociaż rzeczy już nie powiodły. Nie ma sposobu, aby dzwoniący wiedział, jakie błędy się wydarzyły.

  6. Nie ma możliwości wiedzieć, kiedy wszystko się skończy.

Oto wersja twojego kodu, który wykorzystuje obietnice prawidłowo sekwencji, propaguje odpowiednie propaga wszystkich błędów i powiedz, kiedy wszystko się skończy. Nie znam bibliotek {x0}} i Jimp, więc skonsultowałem się z dokumentacją, aby zobaczyć, jak używać ich z obietnicami (oba wydają się obsługują wbudowany wbudowany). Użyłem biblioteki obietnicy Bluebird, aby dać mi obiecując wsparcie dla biblioteki {x2}} i skorzystać z Promise.map(), co jest wygodne tutaj.

Jeśli nie chcesz korzystać z Bluebird Promise Library, można założyć moduł fs innych sposobów lub zdarzenia Promisify indywidualne metody fs, które chcesz użyć z obietnicami. Ale kiedy przyzwyczaisz się do prowadzenia programowania asynchronizacji z obietnicami, będziesz chciał użyć go do wszystkich pracy {x2}}.

Jest to oczywiście nieświadomie (żaden sposób, aby go uruchomić), ale mam nadzieję, że dostaniesz ogólny pomysł na to, co próbujemy zrobić.

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const Jimp = require('jimp');
const rp = require('request-promise');

rp(url).then(function(body) {
    var $ = cheerio.load(body);
    var imgNodes = $('#ires td a img');
    // imgNodes is merely an array-like object, sigh.
    // This is purposedly old-school JS because newer stuff doesn't work:
    var urls = [];
    for (let i = 0; i <= 14; i++) {
        let imgNode = imgNodes[i];
        urls.push(imgNode.attribs['src']);
    }
    // console.log(urls);

    const processCompress = function() {
        return fs.mkdirAsync(path.join(__dirname, 'Photos', query).then(function(error) {
            let j = 0;
            return Promise.map(urls, function(url) {
                var source = tinify.fromUrl(url);
                return source.toFile(path.join(__dirname, 'Photos', query, "optimized_" + ++j + ".jpg"));
            });
        });
    });

    const getFiles = function() {
        return fs.readdirAsync(path.join(__dirname, 'Photos', query).then(function(files) {
            return files.map(function(file) {
                return path.join(__dirname, 'Photos', query, file);
            });
        });
    };

    function colourMeBw(fileList) {
        return Promise.map(fileList, function(file) {
            return Jimp.read(file).greyscale().write(file);
        });
    }
    return processCompress().then(getFiles).then(colourMeBw);
}).then(function() {
    // all done here
}).catch(function(err) {
    // error here
});

Twoja zmienna query Używana tutaj nie wydaje się być zdefiniowana w dowolnym miejscu, więc zakładam, że jest zdefiniowany w wyższym zakresie.

Należy pamiętać, że jedna duża zaleta korzystania z obietnic dla wielostopniowego działania jest tak, że wszystkie błędy kończą się w jednym miejscu, bez względu na to, gdzie wystąpiły w ogólnym procesie wielopoziomowym.


Uwaga: Jeśli przetwarzasz dużą liczbę obrazów lub średniej liczby dużych obrazów, może to zakończyć się przy użyciu uczciwej ilości pamięci, ponieważ ten kod przetwarza wszystkie obrazy równolegle. Jedną z zalet bluebird {x x0}} jest to, że ma opcjonalną opcję concurrency, która określa, ile żądań powinno być "w locie" jednocześnie. Możesz wybrać to do średniego numeru, aby kontrolować wykorzystanie pamięci w razie potrzeby.

Lub można zmienić strukturę tak, że zamiast skompresować wszystko, a następnie przekonwertować wszystkie do Greyscale, możesz kompresować jeden, przekonwertować skalę szarą, a następnie przejść do następnej, itp ...

0
jfriend00 25 czerwiec 2017, 08:36

Myślałem, że wezwanie do rozwiązania () bezpośrednio po tym, jak FSRMKdir jest nie tak, ponieważ MKdir pracuje ASNYC, więc osiągnięto rozstrzygnięcie bez wykonywania całej pracy MKDir.

const processCompress = new Promise(resolve => {
            fs.mkdir(path.join(__dirname, 'Photos', query), function(error) {
                let j = 0;
                if (!error) {
                    for (i in urls) {
                        console.log(i);
                        var source = tinify.fromUrl(urls[i]);
                        source.toFile(path.join(__dirname, 'Photos', query, "optimized_" + ++j + ".jpg"));
                    }
                }

                resolve(); // <---- inside callback from mkdir.
            });
            // call the resolve from inside the mkDirs-callback function
            // resolve();
        });  

Mam nadzieję, że naprawi twój problem.

0
Planetary Dev 25 czerwiec 2017, 07:57

Przeczytałem kod i myślę, co próbujesz zrobić, to coś takiego:

const cheerio = require("cheerio");
const fetch = require("node-fetch");
const tinify = require("tinify");
const fs = require("fs");
const path = require("path");

const getImages = url => {
  return fetch(url)
    .then(responseToText)
    .then(bodyToImageUrls)
    .then(makePhotoDirectory)
    .then(downloadFiles)
    .then(processImageData)
    .then(doImageManipulation)
    .catch(error => {
      console.log("We’ve encountered an error: " + error);
    });
};

const responseToText = res => res.text();

const bodyToImageUrls = body => {
  const $ = cheerio.load(body);
  return $("img").attr("src");
};

const imgNodesToUrls = imgNodes => {
  return imgNodes.map(imgNode => imgNode.name);
};

const makePhotoDirectory = urls => {
  const dir = path.join(__dirname, "Photos");

  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  return urls;
};

const downloadFiles = urls => {
  /* 
    I could not run this since I don't have a Tinify API key
    but I assume that it returns a promise.
  */
  const promises = urls.map(url => tinify.fromUrl(url));
  return Promise.all(promises);
};

const processImageData = imageData => {
  const promises = imageData.map((data, i) => {
    const fileUrl = path.join(__dirname, "Photos", `optimized_${i}.jpg`);
    return data.toFile(fileUrl);
  });

  return Promise.all(promises);
};

const doImageManipulation = images => {
  // Add your image manipulation here
};
0
Fredrik Christenson 25 czerwiec 2017, 06:37