Консультация № 182694
02.04.2011, 12:13
64.74 руб.
0 18 1
Здравствуйте, уважаемые эксперты! Прошу вас ответить на следующий вопрос:

кодирую бинарные данные с помощью base64 (компонент TIdEncoderMIME) и результат декодирую, но он оказывается порченным - не соответствует оригиналу. вот код:

Код:
var
Buf: ^TBytes;
s:string;
...
AssignFile(xls, 'data.xls');
Reset(xls, 1);
xls_size:= FileSize(xls);
buf := AllocMem(xls_size);
BlockRead(xls, Buf^, xls_size, readed); // считываю весь двоичный файл в buf
s:= IdEncoderMIME1.EncodeBytes(tbytes(buf)); // кодирую
s:= IdDecoderMIME1.DecodeString(s)// тут же декодирую и на выходе получаю порченные данные - не совпадают с исходным файлом 'data.xls';


EncodeBytes() требует параметр с типом TBytes, но у меня динамический массив TBytes, поэтому передал параметром tbytes(buf). так же попробовал сделать так:
Код:
Response.Content:= IdEncoderMIME1.EncodeBytes(buf^);

выкинуло ошибку: Access violation at address 0040950C in module
и без ^ не компилится:
Код:
Response.Content:= IdEncoderMIME1.EncodeBytes(buf);

Обсуждение

давно
Профессионал
153662
1070
03.04.2011, 10:12
общий
Можно посмотреть весь Ваш исходник?
s:= IdEncoderMIME1.EncodeBytes(tbytes(buf)); // кодирую s:= IdDecoderMIME1.DecodeString(s)//
Я думаю если кодируешь IdEncoderMIME1.EncodeBytes, то и декодировать нужно соответственной функцией. Какая версия Делфи у Вас?
Об авторе:
Мои программы со статусом freeware для Windows на моём сайте jonix.ucoz.ru

Неизвестный
03.04.2011, 11:07
общий
Адресаты:
Это весь исходник. нехватает только несколько переменных в объявлении:
xls: File;
xls_size, readed: integer;

использую delphi 2010
давно
Мастер-Эксперт
325460
1469
04.04.2011, 18:54
общий
во первых не помню учитывается ли регистр (большая. маленькая) буквы в делфи, во вторых вы указатель преобразовываете в байт последовательность, соответственно при расшифровке вы видите значение указателя
не могу найти описание компонента. если бы было под рукой описание, то мож чего дельного и посоветовал бы.

http://www.delphimaster.net/view/1-1222762776/all
Об авторе:
to live is to die
давно
Мастер-Эксперт
425
4118
05.04.2011, 06:45
общий
Адресаты:
Цитата: CradleA
не помню учитывается ли регистр (большая. маленькая) буквы в делфи

Нет, не учитывается.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
давно
Мастер-Эксперт
425
4118
06.04.2011, 07:55
общий
это ответ
Здравствуйте, Maksim Trofimov!

Поскольку у Вас версия Delphi 2010, то и Indy у Вас должна быть версии 10 и в ней есть более удобный способ работы с кодированием\декодированием файлов, чем это было ранее. Вот пример:
Код:
//Кодирование
procedure TForm1.Button1Click(Sender: TObject);
Var
s1, s2: TFileStream;
begin
if od1.Execute then
Begin
s1:=TFileStream.Create(od1.FileName, fmOpenRead);
s2:=TFileStream.Create(od1.FileName+'.mime', fmCreate);
en1.Encode(s1, s2);
s1.Free;
s2.Free;
End;
end;

//Декодирование
procedure TForm1.Button2Click(Sender: TObject);
Var
s1, s2: TFileStream;
begin
if od1.Execute then
Begin
s1:=TFileStream.Create(od1.FileName, fmOpenReadWrite);
s2:=TFileStream.Create(Copy(od1.FileName, 1, Length(od1.FileName)-5), fmCreate);
de1.DecodeBegin(s2);
de1.Decode(s1);
de1.DecodeEnd;
s1.Free;
s2.Free;
End;

end;

В этом примере en1 - это компонент типа TIdEncoderMIME, а de1 - компонент типа TIdDecoderMIME. od1 - это компонент-диалог открытия файла, с помощью него задаётся тот файл, который надо закодировать\раскодировать.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
06.04.2011, 10:07
общий
Адресаты:
да, я так делал раньше и видел, что кодируется-декодируется правильно, но проблема в том, что мне нужно получить закодированную стоку вставить ее в xml-тэг и отправлять по запросу через интернет по http...
я пытался вставить открывающий тэг в пустой стрим, потом вставить закодированный бинарник из стрима, и потом вставить закрывающую часть тэга. и так же наткнулся на ошибку. у меня буквы тэга чередовались через null. проблема до сих пор не решена.
давно
Мастер-Эксперт
425
4118
06.04.2011, 10:57
общий
Тогда, вместо второго потока (DestinationStream), Вы подставляете поток-строку:
Код:
//Кодирование
procedure TForm1.Button1Click(Sender: TObject);
Var
s1: TFileStream;
ss: TStringStream;
begin
if od1.Execute then
Begin
s1:=TFileStream.Create(od1.FileName, fmOpenRead);
ss:=TStringStream.Create;
en1.Encode(s1, ss);
//Далее, проводите операции по записи полученной строки в файл xml
...
WriteLn(ФайлXML, ss.ReadString(ss.Size));
...
s1.Free;
ss.Free;
End;
end;

Цитата: 31543
у меня буквы тэга чередовались через null.

Видимо Вы совершенно забыли, что, начиная с версии 2009, Delphi в строках использует кодировку UTF-16, в то время как xml-файлы работают исключительно с кодировкой UTF-8.
Мой Вам совет - переходите на Lazarus. В нём стандартная кодировка - UTF-8, полностью кроссплатформенная штука.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
давно
Мастер-Эксперт
425
4118
07.04.2011, 09:07
общий
Скажите, Вас мой ответ с комментариями в мини-форуме устраивает или что-то не так?
Дело в том, что не зная, каким образом Вы работаете с XML файлами, я не могу внести необходимые правки в ответ.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
07.04.2011, 10:08
общий
Адресаты:
мне нужно xml документ отправить по http
я создавал стрим в котором создавал этот документ и отправлял по get-запросу:
Response.ContentStream:= s;
где s - стрим с xml документом
в итоге получал проблему связанную, как вы говорили, с кодировкой utf-16
у меня долго не получалось я и забил
делал как-то так:
Код:
        ss1:= TMemoryStream.Create;
ss2:= TMemoryStream.Create;
ss1.LoadFromFile('1.xlsx'); // файл который в base64 должен быть
EncodeStream(ss1, ss2);

ss1.Clear;
ss1.Write('<xml>', 2*5);

ss2.Position:=0;
ss1.CopyFrom(ss2, ss2.Size);

ss1.Write('</xml>', 2*6);

Response.ContentStream:= ss1;

давно
Мастер-Эксперт
425
4118
07.04.2011, 11:36
общий
Сам xml-документ Вы формируете неправильно. Про кодировку я Вам уже говорил. Кроме того, обязательным атрибутом такого документа является заголовок. Минимальный правильный заголовок такой:
<?xml version="1.0"?>

Далее идут теги, в которых заключаются данные документа.
Что у Вас должно получиться в результате? Что-то вроде этого:
Код:
<?xml version="1.0"?>
<file>Тары-бары-растабары-Ваш-закодированный-файл</file>


Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
давно
Мастер-Эксперт
425
4118
07.04.2011, 11:51
общий
И давайте ка, всё же, поработаем с потоком-строкой, т.к. из неё легче сделать строку в нужной кодировке.
Код:
//Кодирование
procedure TForm1.Button1Click(Sender: TObject);
Var
s1: TFileStream;
ss1, ss2: TStringStream;
sutf8: TUTF8String;
begin
if od1.Execute then
Begin
sutf8:='<?xml version="1.0"?><file>';
s1:=TFileStream.Create(od1.FileName, fmOpenRead);
ss1:=TStringStream.Create;
ss2:=TStringStream.Create(sutf8);
en1.Encode(s1, ss1);
ss2.Seek(ss2.Size);
ss2.CopyFrom(ss1);
ss2.WriteString('</file>');
Response.ContentStream:= ss2;
s1.Free;
ss1.Free;
ss2.Free;
End;
end;
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
07.04.2011, 15:14
общий
Адресаты:
Код:
    sutf8:='<?xml version="1.0"?><file>';
s1:=TFileStream.Create('1.xlsx', fmOpenRead);
ss1:=TStringStream.Create;
ss2:=TStringStream.Create(sutf8);
en1.Encode(s1, ss1);
//ss2.CopyFrom(ss1, ss1.Size); // здесь ошибка Stream read error, если закоментить, то будет ошибка Status: 500 Invalid pointer
ss2.WriteString('</file>');
Response.ContentType:='text/xml';
Response.ContentStream:= ss2;
s1.Free;
ss1.Free;
ss2.Free;
давно
Мастер-Эксперт
425
4118
08.04.2011, 05:35
общий
//ss2.CopyFrom(ss1, ss1.Size); // здесь ошибка Stream read error, если закоментить, то будет ошибка Status: 500 Invalid pointer ss2.WriteString('</file>');

Простите, это я Вам неправильно написал. Поддался дурному влиянию копипаста.
Для TStringStream почему-то метод CopyFrom() не работает. Хотя должен бы... Нужно делать вот так:
Код:
//Кодирование
procedure TForm1.Button1Click(Sender: TObject);
Var
s1: TFileStream;
ss1, ss2: TStringStream;
sutf8: UTF8String;
begin
if od1.Execute then
Begin
sutf8:='<?xml version="1.0"?><file>';
s1:=TFileStream.Create(od1.FileName, fmOpenRead);
ss1:=TStringStream.Create;
ss2:=TStringStream.Create(sutf8);
en1.Encode(s1, ss1);
ss2.Seek(ss2.Size);
ss2.WriteString(ss1.DataString); //!!Обратите внимание!!
sutf8:='</file>';
ss2.WriteString(sutf8);
Response.ContentStream:= ss2;
s1.Free;
ss1.Free;
ss2.Free;
End;
end;
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
08.04.2011, 15:43
общий
Адресаты:
Код:
    sutf8:='<?xml version="1.0"?><file>';
s1:=TFileStream.Create(www_dir+xls_cach_dir+caching_xls, fmOpenRead);
ss1:=TStringStream.Create;
ss2:=TStringStream.Create(sutf8);
en1.Encode(s1, ss1);
ss2.Position:= ss2.Size; // ничего не меняет
ss2.WriteString(ss1.DataString);
sutf8:='</file>';
ss2.WriteString(sutf8);
Response.ContentType:='text/xml';
Response.ContentStream:= ss2;
s1.Free;
ss1.Free;
ss2.Free;

не работает. ошибка тажа: Status: 500 Invalid pointer operation
давно
Мастер-Эксперт
425
4118
08.04.2011, 16:10
общий
Цитата: 31543
ss2.Position:= ss2.Size; // ничего не меняет
...
не работает. ошибка тажа: Status: 500 Invalid pointer operation

По поводу этого Вам лучше пообщаться с разработчиками из Embarcadero. Здесь на подобные вопросы никто ответа не даст. Я Вам дал 100%-но рабочий код, это проверено. Вы его изменили. Отчего выскакивает эта ошибка именно при таком применении - ответить затрудняюсь, тем более, что Ваш код неполный. Не исключено, что Вы совершаете ошибку где-то на предыдущем этапе, ведь в сообщении об ошибке компилятор Вам указывает точное место, где она совершена. А здесь Вы полностью сообщение привести не потрудились.
Извините, Ваших данных для анализа и ответа недостаточно.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
08.04.2011, 16:56
общий
Адресаты:
я весь код привел, исключая значения переменных, которые составляют путь к открываемому файлу:
Код:
s1:=TFileStream.Create(www_dir+xls_cach_dir+caching_xls, fmOpenRead);

в пути ошибки нет

на эту строку ругался, что не хватает параметров:
Код:
ss2.Seek(ss2.Size);

изменил на:
Код:
ss2.Seek(ss2.Size, soFromBeginning);

но это равносильно этому:
Код:
ss2.Position:= ss2.Size;


компилятор не указывает место ошибки, так как прога работает по заданным get запросам, которые подаются через браузер
а не запускается в среде.

пока писал это сообщение, нашелся ответ
меняю:
Код:
Response.ContentStream:= ss2;

на:
Код:
Response.Content:= ss2.DataString;


надо было мне изначально задавать get параметры статические для отладки через среду...
почему не работает первый вариант для меня странно. возможно там присваивается указатель и память очищается раньше, чем надо
код расположен в функции:
Код:
procedure Twm.WebModule1rootAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);


sir Henry, спасибо за помощь.
давно
Мастер-Эксперт
425
4118
08.04.2011, 18:20
общий
Адресаты:
Цитата: 31543
Response.Content

Оказывается Content имеет тип String, а не TStream поэтому первый вариант и не рабочий, а Response.Content:= ss2.DataString - это и есть правильный вариант.
Код:
ss2.Seek(ss2.Size, soFromBeginning);

но это равносильно этому:
Код:
ss2.Position:= ss2.Size;

Тайна, покрытая мраком.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
08.04.2011, 18:37
общий
Адресаты:
да нет же. первый вариант это:
Код:
Response.ContentStream:= ss2;

типы совпадают
Форма ответа