Я работаю над приложением Laravel (v5.7), которое преобразует загруженный CSV (с контактами) в массив, который затем передается в качестве аргумента при отправке класса задания.
Вот пример файла CSV (поддерживаемый формат):
123456,Richard,Smith
654321,John,Doe
Загруженный (CSV) файл обрабатывается следующим образом:
$file_path = $request->file_name->store('contacts');
$file = storage_path('app/' . $file_path);
$contactsIterator = $this->getContacts($file);
$contacts = iterator_to_array($contactsIterator); // Array of contacts from uploaded CSV file
protected function getContacts($file)
{
$f = fopen($file, 'r');
while ($line = fgets($f))
{
$row = explode(",", $line);
yield [
'phone' => !empty($row[0]) ? trim($row[0]) : '',
'firstname' => !empty($row[1]) ? trim($row[1]) : '',
'lastname' => !empty($row[2]) ? trim($row[2]) : '',
];
}
}
Наконец, массив $contacts
передается отправляемому заданию:
ImportContacts::dispatch($contacts);
Этот класс задания выглядит следующим образом:
public function __construct($contacts)
{
Log::info('ImportContacts@__construct START');
$this->contacts = $contacts;
Log::info('ImportContacts@__construct END');
}
public function handle()
{
Log::info('ImportContacts@handle');
}
... и все работает нормально (нетошибки), пока я не попробовал с этим CSV:
123456,Richardÿ,Smith
654321,John,Doe
Пожалуйста, обратите внимание ÿ
. Итак, когда я пытаюсь с этим CSV - я получаю это исключение ошибки:
/code_smsto/vendor/laravel/framework/src/Illuminate/Queue/Queue.php | 91 | Unable to JSON encode payload. Error code: 5
... и мой файл журнала выглядит так:
error local 2019-11-11 17:17:18 /code_smsto/vendor/laravel/framework/src/Illuminate/Queue/Queue.php | 91 | Unable to JSON encode payload. Error code: 5
info local 2019-11-11 17:17:18 ImportContacts@__construct END
info local 2019-11-11 17:17:18 ImportContacts@__construct START
Как вы можете видеть - *Метод 1027 * никогда не выполнялся. Если я удалю ÿ
- без ошибок и handle
выполняется.
Я пытался решить эту проблему, но безуспешно:
- Применить
utf8_encode
:
protected function getContacts($file, $listId)
{
$f = fopen($file, 'r');
while ($line = fgets($f))
{
$row = explode(",", $line);
yield [
'phone' => !empty($row[0]) ? utf8_encode($row[0]) : '',
'firstname' => !empty($row[1]) ? utf8_encode($row[1]) : '',
'lastname' => !empty($row[2]) ? utf8_encode($row[2]) : '',
];
}
}
... и это работает (без ошибок, независимо от того, есть ли ÿ
), но тогда греческие и кириллические буквы превращаются в вопросительные знаки. Например, это: Εθνικής
станет ???????
.
Я также пытался с mb_convert_encoding($row[1], 'utf-8')
- и это не превращает греческую или кириллическую букву в вопросительные знаки, но этот символ ÿ
станет?
.
Переместить «обработку» (преобразование в массив) загруженного файла CSV в
@handle
метод класса Job сработал, но тогда я не смог сохранить данные из этого массива в БД (MongoDB). Пожалуйста, смотрите обновление ниже.
ОТЛАДКА:
Вот что я получаю от dd($contacts);
:
Итак, у него есть то "b", где ÿ
. И после некоторого «прибегания к поиску» я обнаружил, что это «b» означает «двоичную строку», то есть строку, не являющуюся юникодом, в которой функции работают на уровне байтов ( Что делает b перед строковыми литералами? ).
Что я понимаю, так это то, что: при отправке класса Job, Laravel пытается "JSON-кодировать" его (передаваемые аргументы / данные), но он терпит неудачу из-за наличия двоичных данных (не-Unicode строк). Во всяком случае, я не смог найти решение (чтобы иметь возможность обрабатывать такой файл CSV с ÿ
).
Я использую:
- Laravel 5.7
- PHP 7.1.31-1 + ubuntu16.04.1 + deb.sury.org + 1 (cli) (построено: 7 августа 2019 10:22:48) (NTS)
- Очереди с поддержкой Redis
ОБНОВЛЕНИЕ
Когда я перемещаю "обработку" (преобразование в массив) загруженного файла CSV в @handle
метод класса Job - я не получаю эту ошибку (Unable to JSON encode payload. Error code: 5
), но когда я пытаюсь сохранить эти проблемные двоичные данные с ÿ
(b"Richardÿ"
) в MongoDB - происходит сбой. Странно то, что я не получаю никаких исключений ошибок сообщение в файле журнала, поэтому я помещаю все в try-catch следующим образом:
try {
// Insert data into MongoDB
} catch (Exception $e) {
Log::info($e->getFile());
Log::info($e->getLine());
Log::info($e->getMessage());
}
... и этоэто результат:
В любом случае, я считаю, что это не удалось из-за b"Richardÿ"
, и я предполагаю, что решение заключается в кодировании строки, нокак я уже упоминал - я не смог найти решение, которое работает:
utf8_encode
работает (без ошибок, независимо от того, есть ли ÿ
), но затем греческие и кириллические буквыпревращаются в вопросительные знаки. Например, это: Εθνικής
станет ???????
mb_convert_encoding($row[1], 'utf-8')
- это не превращает греческую или кириллическую букву в вопросительные знаки, но этот символ ÿ
станет ?
. iconv('windows-1252', 'UTF-8', $row[1])
- работает (без ошибок, независимо от того, есть ли ÿ
), но при наличии букв греческого или кириллического алфавита - происходит сбой (я получаю это исключение ошибки: iconv(): Detected an illegal character in input string
)