I am using HttpCli component form ICS to upload files using Delphi 7 ..
i copied code from example in the ICS folder .. tried to edit to let me upload files but got stuck on how to add the file to the file field name (filedata=c:\1.rar)
var
lData: TStringStream;
lDataOut : TMemoryStream;
lResultPage: string;
HttpCli1: THttpCli;
srv : string;
Data : AnsiString;
begin
HttpCli1 := THttpCli.Create(nil);
lDataOut := TMemoryStream.Create;
lData := TStringStream.Create('');
//A post request would use lDataout to post data to server
lDataOut.Seek(0, soFromBeginning);
httpcli1.SendStream := lDataOut;
httpcli1.RcvdStream := lData;
httpcli1.Cookie := memo2.Text;
httpcli1.URL := 'http://www.datafile.com/index.html';
try
httpcli1.get;
//lResultPage will contain the answer from the webserver as a string
lResultPage := lData.DataString;
// srv will contain upload URL ...
srv := ExtractBetween(lResultPage,'upload_url: "','"');
Data := 'Filename=' + UrlEncodeToA('Chrysanthemum.rar') + '&' +
'upload_type=' + UrlEncodeToA('file') + '&' +
'folder_id=0' + '&'+
'Filedata=' + '&' +
'Upload=' + UrlEncodeToA('Submit Query');
HttpCli1.SendStream := TMemoryStream.Create;
HttpCli1.SendStream.Write(Data[1], Length(Data));
HttpCli1.SendStream.Seek(0, 0);
HttpCli1.RcvdStream := lData;
HttpCli1.URL := srv;
HttpCli1.ContentTypePost := 'application/octet-stream';
HttpCli1.PostAsync;
except
lDataOut.Free;
Exit;
end;
lDataOut.Free;
lData.Free;
httpcli1.Free ;
end;
this how it should be ..
So I don't know how i add the file to the "fileData" in the postdata ...
Related
How can I add a large text file (actually a HTML file) as an attachment to a channel in Slack? A working example would be great. I use SDriver - the included demo works fine to send some strings, but I don't find anything on how to use attachments.
What I did so far:
procedure TForm1.SendActionExecute(Sender: TObject);
var
LWebHook: IMessageBuffer;
LMessage: IMessage;
LStopWatch: TStopWatch;
vAttachment: IAttachment;
vField: IFields;
begin
LStopWatch := TStopWatch.StartNew;
LMessage := TMessage.Create(EditMessage.Text + ' [' + TimeToStr(Now) + ']');
LMessage.UserName := EditUserName.Text;
LMessage.Icon_URL := EditIcon_URL.Text;
LMessage.Icon_Emoji := EditIcon_Emoji.Text;
LMessage.Channel := EditChannel.Text;
vAttachment := LMessage.AddAttachment;
vField := vAttachment.AddFields;
vField.Title := 'Title';
vField.Value := 'Value';
///////////////////////////////////////////////////////
// How can I add a large text file as attachment here ?
///////////////////////////////////////////////////////
LWebHook := TIncomingWebHook.Create(EditWebHookURL.Text, False);
LWebHook.Push(LMessage);
LWebHook.Flush;
end;
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm developing in Delphi 7 a windows desktop software using Dropbox API V2.
My code doesn't work. Please provide me a correct examle Delphi code!
Thanks!
My code snippet:
procedure TDropbox.Upload(SourceFileName: String; DropBoxFileName: String; FTimeout: Integer = 5000);
const
FDropBoxAppAccessToken = 'xxxxxxxxxxxxxxxxxxxx';
var
FHTTPS: TIdHTTP;
FStream: TFileStream;
FDropBoxResponseCode: Integer;
FHTTPResponse: String;
begin
FHTTPRequestURL := 'https://api-content.dropbox.com/2/files/upload';
FStream := TFileStream.Create(SourceFileName, fmOpenRead);
FStream.Position := 0;
FHTTPS := TIdHTTP.Create(nil);
FHTTPS.ConnectTimeout := FTimeout;
FDropBoxResponseCode := 0;
FHTTPResponse := '';
params := TStringList.Create;
arg := 'Dropbox-API-Arg:{"path:"' + FDropBoxBaseAppPath + DropBoxFileName + '}';
try
FHTTPS.Request.CustomHeaders.Add('Authorization:Bearer ' + FDropBoxAppAccessToken);
FHTTPS.Request.CustomHeaders.AddStrings := '(Dropbox-API-Arg:{"path:"' + FDropBoxBaseAppPath + DropBoxFileName + '}');
FHTTPS.Request.CustomHeaders.Values[arg];
FHTTPS.Request.ContentType := 'application/octet-stream';
FHTTPS.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(FHTTPS);
FHTTPResponse := FHTTPS.Post(FHTTPRequestURL, FStream);
except on E: Exception do
begin
end;
end;
Dropbox server always says:
400 Bad request, the source file is exists.
Please help!
You are sending the request to the wrong URL, you are misusing the Request.CustomHeaders property, and you are malforming your JSON data.
Try sonething more like this instead:
procedure TDropbox.Upload(const SourceFileName: String; const DropBoxFileName: String; Timeout: Integer = 5000);
const
FDropBoxAppAccessToken = 'xxxxxxxxxxxxxxxxxxxx';
FHTTPRequestURL := 'https://content.dropboxapi.com/2/files/upload';
var
FHTTPS: TIdHTTP;
FStream: TFileStream;
FDropBoxResponseCode: Integer;
FHTTPResponse: String;
begin
FDropBoxResponseCode := 0;
try
FStream := TFileStream.Create(SourceFileName, fmOpenRead or fmShareDenyWrite);
try
FHTTPS := TIdHTTP.Create(nil);
try
FHTTPS.ConnectTimeout := Timeout;
FHTTPS.Request.CustomHeaders.Values['Authorization'] := 'Bearer ' + FDropBoxAppAccessToken;
FHTTPS.Request.CustomHeaders.Values['Dropbox-API-Arg'] := '{"path": "' + FDropBoxBaseAppPath + DropBoxFileName + '"}';
FHTTPS.Request.ContentType := 'application/octet-stream';
FHTTPS.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(FHTTPS);
FHTTPResponse := FHTTPS.Post(FHTTPRequestURL, FStream);
finally
FHTTPS.Free;
end;
finally
FStream.Free;
end;
except
on E: Exception do begin
end;
end;
end;
i want to send emoji with indy 9.00.10 on delphi 7. i use tnt VCL Controls .
i found this url http://apps.timwhitlock.info/emoji/tables/unicode for unicode and bytes code.
how to convert this codes to delphi Constants for Send with indy.
i use this delphi code for send message to telegram bot:
procedure TBotThread.SendMessage(ChatID:String; Text : WideString;
parse_mode:string;disable_notification:boolean);
Var
Stream: TStringStream;
Params: TIdMultipartFormDataStream;
//Text : WideString;
msg : WideString;
Src : string;
LHandler: TIdSSLIOHandlerSocket;
begin
try
try
if FShowBotLink then
Text := Text + LineBreak + FBotUser;
msg := '/sendmessage';
Stream := TStringStream.Create('');
Params := TIdMultipartFormDataStream.Create;
Params.AddFormField('chat_id',ChatID);
if parse_mode <> '' then
Params.AddFormField('parse_mode',parse_mode);
if disable_notification then
Params.AddFormField('disable_notification','true')
else
Params.AddFormField('disable_notification','false');
Params.AddFormField('disable_web_page_preview','true');
Params.AddFormField('text',UTF8Encode(Text));
LHandler := TIdSSLIOHandlerSocket.Create(nil);
FidHttpSend.ReadTimeout := 30000;
FidHttpSend.IOHandler:=LHandler;
LHandler.SSLOptions.Method := sslvTLSv1;
LHandler.SSLOptions.Mode := sslmUnassigned;
FidHttpSend.HandleRedirects := true;
FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);
finally
Params.Free;
Stream.Free;
ENd;
except
on E: EIdHTTPProtocolException do
begin
if E.ReplyErrorCode = 403 then
begin
WriteToLog('Bot was blocked by the user');
end;
end;
end;
end;
bytes sample for emojies:
AERIAL_TRAMWAY = '\xf0\x9f\x9a\xa1';
AIRPLANE = '\xe2\x9c\x88';
ALARM_CLOCK = '\xe2\x8f\xb0';
ALIEN_MONSTER = '\xf0\x9f\x91\xbe';
sorry for bad english!!!
The Telegram Bot API supports several forms of input:
We support GET and POST HTTP methods. We support four ways of passing parameters in Bot API requests:
URL query string
application/x-www-form-urlencoded
application/json (except for uploading files)
multipart/form-data (use to upload files)
You are using the last option.
Indy 9 does not support Delphi 2009+ or Unicode. All uses of string are assumed to be AnsiString, which is the case in Delphi 7. Any AnsiString you add to TIdMultipartFormDataStream or TStrings, even a UTF-8 encoded one, will be transmitted as-is by TIdHTTP. However, there is no option to specify to the server that the string data is actually using UTF-8 as a charset. But, according to the docs:
All queries must be made using UTF-8.
So not specifying an explicit charset might not be problem.
If you still have problems with multipart/form-data, then consider using application/x-www-form-urlencoded (use TIdHTTP.Post(TStrings)) or application/json (use TIdHTTP.Post(TStream)) instead:
procedure TBotThread.SendMessage(ChatID: String; Text: WideString; parse_mode: string; disable_notification: boolean);
var
Params: TStringList;
LHandler: TIdSSLIOHandlerSocket;
begin
if FShowBotLink then
Text := Text + LineBreak + FBotUser;
Params := TStringList.Create;
try
Params.Add('chat_id=' + UTF8Encode(ChatID));
if parse_mode <> '' then
Params.Add('parse_mode=' + UTF8Encode(parse_mode));
if disable_notification then
Params.Add('disable_notification=true')
else
Params.Add('disable_notification=false');
Params.Add('disable_web_page_preview=true');
Params.Add('text=' + UTF8Encode(Text));
LHandler := TIdSSLIOHandlerSocket.Create(nil);
try
LHandler.SSLOptions.Method := sslvTLSv1;
LHandler.SSLOptions.Mode := sslmClient;
FidHttpSend.HandleRedirects := true;
FidHttpSend.ReadTimeout := 30000;
FidHttpSend.IOHandler := LHandler;
try
try
FidHttpSend.Post(BaseUrl + API + '/sendmessage', Params, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ReplyErrorCode = 403 then
begin
WriteToLog('Bot was blocked by the user');
end;
end;
end;
finally
FidHttpSend.IOHandler := nil;
end;
finally
LHandler.Free;
end;
finally
Params.Free;
end;
end;
procedure TBotThread.SendMessage(ChatID: String; Text: WideString; parse_mode: string; disable_notification: boolean);
var
Params: TStringStream;
LHandler: TIdSSLIOHandlerSocket;
function JsonEncode(const wStr: WideString): string;
var
I: Integer;
Ch: WideChar;
begin
// JSON uses UTF-16 text, so no need to encode to UTF-8...
Result := '';
for I := 1 to Length(wStr) do
begin
Ch := wStr[i];
case Ch of
#8: Result := Result + '\b';
#9: Result := Result + '\t';
#10: Result := Result + '\n';
#12: Result := Result + '\f';
#13: Result := Result + '\r';
'"': Result := Result + '\"';
'\': Result := Result + '\\';
'/': Result := Result + '\/';
else
if (Ord(Ch) >= 32) and (Ord(Ch) <= 126) then
Result := Result + AnsiChar(Ord(wStr[i]))
else
Result := Result + '\u' + IntToHex(Ord(wStr[i]), 4);
end;
end;
end;
begin
if FShowBotLink then
Text := Text + LineBreak + FBotUser;
Params := TStringStream.Create('');
try
Params.WriteString('{');
Params.WriteString('chat_id: "' + JsonEncode(ChatID) + '",');
if parse_mode <> '' then
Params.WriteString('parse_mode: "' + JsonEncode(parse_mode) + '",')
if disable_notification then
Params.WriteString('disable_notification: True,')
else
Params.WriteString('disable_notification: False,');
Params.WriteString('disable_web_page_preview: True,');
Params.WriteString('text: "' + JsonEncode(Text) + '"');
Params.WriteString('}');
Params.Position := 0;
LHandler := TIdSSLIOHandlerSocket.Create(nil);
try
LHandler.SSLOptions.Method := sslvTLSv1;
LHandler.SSLOptions.Mode := sslmClient;
FidHttpSend.HandleRedirects := true;
FidHttpSend.ReadTimeout := 30000;
FidHttpSend.IOHandler := LHandler;
try
try
FidHttpSend.Request.ContentType := 'application/json';
FidHttpSend.Post(BaseUrl + API + '/sendmessage', Params, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ReplyErrorCode = 403 then
begin
WriteToLog('Bot was blocked by the user');
end;
end;
end;
finally
FidHttpSend.IOHandler := nil;
end;
finally
LHandler.Free;
end;
finally
Params.Free;
end;
end;
That being said, your function's Text parameter is a WideString, which uses UTF-16, so you should be able to send any Unicode text, including emojis. If you are trying to generate text in your code, just make sure you UTF-16 encode any non-ASCII characters correctly. For example, codepoint U+1F601 GRINNING FACE WITH SMILING EYES is wide chars $D83D $DE01 in UTF-16:
var
Text: WideString;
Text := 'hi ' + #$D83D#$DE01; // 'hi 😁'
SendMessage('#channel', Text, 'Markup', False);
Alternatively, you can use HTML in your text messages, so you can encode non-ASCII characters using numerical HTML entities. According to the docs:
All numerical HTML entities are supported.
Codepoint U+1F601 is numeric entity $#128513; in HTML:
var
Text: WideString;
Text := 'hi $#128513;'; // 'hi 😁'
SendMessage('#channel', Text, 'HTML', False);
I'm attempting to work with the Google Drive API in Delphi XE2, and thus far, I have just about everything working. One thing I'm struggling with is the multipart upload. When I attempt to upload a file, I get a 503 error. Normally, that should indicate a problem with the server. However, when I send an upload request with the same body and headers to the same URL using fiddler rather than my API, the file is uploaded successfully. This tells me there has to be a problem with my code. This particular function is a mess, but here it is.
function TGoogleDriveApi.MultipartUpload(aStream : TStream; aTitle : string = 'Untitled';
aDescription : string = ''; mimeType : string = 'application/octet-stream';
indexableText : IGoogleDriveIndexableText = nil;
lastViewedByMeDate : string = ''; modifiedDate : string = '';
parents : IGoogleDriveParentList = nil) : IGoogleDriveFile;
var
json, url, body, boundry, contentType : string;
ss : TStringStream;
ms : TMemoryStream;
lHttpHelper : IHttpHelper;
streamBytes : TBytes;
begin
boundry := 'a_boundry';
body := '--'+boundry+sLineBreak;
body := body + 'Content-Type: application/json; charset=UTF-8';
lHttpHelper := THttpHelper.Create;
if(Token.IsExpired) then
Token.Refresh(TokenUrl, OAuth.ClientId, OAuth.ClientSecret);
url := 'https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart&access_token='+lHttpHelper.UrlEncode(Token.access_token);
json := '{';
json := json + '"title": "'+aTitle+'"';
if(aDescription <> '') then
json := json + ', "description": "'+aDescription+'"';
if(lastViewedByMeDate <> '') then
json := json + ', "lastViewedByMeDate": "'+lastViewedByMeDate+'"';
json := json + ', "mimeType": "'+mimeType+'"';
if(modifiedDate <> '') then
json := json + ', "modifiedDate": "'+modifiedDate+'"';
json := json + '}';
body := body + sLineBreak + sLineBreak + json + sLineBreak + sLineBreak + '--'+boundry;
body := body + sLineBreak + 'Content-Type: '+mimeType + sLineBreak + sLineBreak;
body := body + 'some test text from the api' + sLineBreak + sLineBreak + '--'+boundry+'--';
ss := TStringStream.Create;
ss.WriteString(body);
ss.Position := 0;
ms := TMemoryStream.Create;
ms.Write(ss,ss.Size);
SetLength(streamBytes,aStream.Size);
aStream.Read(streamBytes,aStream.Size);
ms.Write(streamBytes[0],aStream.Size);
ss.Clear;
ss.Position := 0;
ss.WriteString(sLineBreak + sLineBreak + '--'+boundry+'--');
ss.Position := 0;
ms.Write(ss,ss.Size);
contentType := 'multipart/related; boundary="'+boundry+'"';
json := lHttpHelper.PostResponse(url,contentType,ms);
FreeAndNil(ss);
FreeAndNil(ms);
Result := nil;
end;
The line that causes problems is the lHttpHelper.PostResponse call. The code for that is shown here:
function THttpHelper.PostResponse(url, contentType : string; aStream : TStream) : string;
var
lHTTP: TIdHTTP;
lStream : TStringStream;
handler : TIdSSLIOHandlerSocketOpenSSL;
begin
lStream := TStringStream.Create;
lHTTP := TIdHTTP.Create(nil);
handler := TidSSLIOHandlerSocketOpenSSL.Create(nil);
handler.SSLOptions.Method := sslvSSLv3;
lHTTP.IOHandler := handler;
try
lHTTP.Request.ContentType := contentType;
lHTTP.Post(url,aStream,lStream);
lStream.Position := 0;
Result := lStream.ReadString(lStream.Size);
//except
finally
lStream.Free;
lHTTP.Free;
handler.Free;
lStream := nil;
lHTTP := nil;
handler := nil;
end;
end;
I'm currently calling the MultipartUpload function from my test, shown here
procedure TestIGoogleDriveApi.TestMultipartUpload;
var
ReturnValue : IGoogleDriveFile;
fs : TFileStream;
begin
fs := TFileStream.Create('testupload.jpg',fmOpenRead);
ReturnValue := FIGoogleDriveApi.MultipartUpload(fs,'Test Multipart Image Upload from API.txt','test file','image/jpeg');
FreeAndNil(fs);
if(ReturnValue = nil) then
fail('ReturnValue cannot be nil');
end;
Any ideas what might be causing the problems? I'm not even sure what to suspect at this point.
You'll need to somehow follow #RobKennedy's advice. You need to see what's happening on the wire to debug this. Try replacing the https drive url with http and then trace with Wireshark.
#Hendra is also correct, that generally your app needs to implement exponential backoff and retry for 500 errors, although I suspect that isn't your specific problem (yet :-))
After changing two things in my code, and doing some substantial cleanup, I was able to get the upload working. The main changes I made were writing everything directly to the TMemoryStream rather than to a TStringStream then to a TMemoryStream and using AnsiStrings rather than strings. Here is my cleaned up code.
function TGoogleDriveApi.MultipartUpload(aStream: TStream; aFile: TGoogleDriveFileUp) : IGoogleDriveFile;
var
json, url, boundary, body : AnsiString;
lHttpHelper : IHttpHelper;
ms : TMemoryStream;
ss : TStringStream;
writer : IRtSerializeWriter;
reader : IRtSerializeReader;
begin
if(Token.IsExpired) then
Token.Refresh(TokenUrl, OAuth.ClientId, OAuth.ClientSecret);
lHttpHelper := THttpHelper.Create;
ss := TStringStream.Create;
writer := TRtJsonSerializeWriter.Create;
writer.SaveToStream(ss,aFile);
ss.Position := 0;
json := ss.ReadString(ss.Size);
FreeAndNil(ss);
url := 'https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart&access_token='+lHttpHelper.UrlEncode(Token.access_token);
boundary := 'a_bondary';
body := '--'+boundary+sLineBreak;
body := body + 'Content-Type: application/json; charset=UTF-8' + sLineBreak + sLineBreak;
body := body + json + sLineBreak + sLineBreak +'--'+boundary;
body := body + sLineBreak + sLineBreak;
ms := TMemoryStream.Create;
ms.Write(body[1],Length(body));
ms.CopyFrom(aStream, aStream.Size);
body := sLineBreak + sLineBreak + '--' + boundary + '--';
ms.Write(body[1],Length(body));
ms.Position := 0;
json := lHttpHelper.PostResponse(url,'multipart/related; boundary="'+boundary+'"',ms);
ss := TStringStream.Create;
ss.WriteString(json);
ss.Position := 0;
reader := TRtJsonSerializeReader.Create;
Result := TGoogleDriveFileDown.Create;
reader.LoadFromStream(ss,Result as TGoogleDriveFileDown);
FreeAndNil(ms);
FreeAndNil(ss);
end;
I'm making requests to the webaddress to get XML files throught the HTTPS connection. But this connection works like 50%. In most cases it fails. Usual error is "socket error #10060". Or "Error connecting with SSL. EOF was observed that violates the protocol". What I'm doing wrong?
function SendRequest(parameters: string): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TStringStream;
xDoc: IXMLDocument;
begin
sPostData := TStringList.Create;
try
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add(parameters);
sHttpSocket := TIdHTTP.Create;
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Request.Method := 'POST';
resStream := TStringStream.Create;
sHttpSocket.Post(Self.sUrl, sPostData, resStream);
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
resStream.Free;
sHttpSocket.Free;
sshSocketHandler.Free;
sPostData.Free;
except on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end
end;
end;
Maybe I can do this in another way, that works like 100%, when internet connection is available?
Regards,
evilone
An "EOF" error suggests you are connnecting to a server that is not actually using SSL to begin with, or the SSL data may be corrupted.
Besides that, why are you including explicit '&' characters between your post data parameters? Don't do that, Indy will just encode them and send its own '&' characters. Also, consider using TMemoryStream instead of TStringStream to ensure IXMLDocumect.LoadFromStream() is loading the server's original raw XML data as-is, and not an altered version that the RTL/VCL produces due to Unicode handling (TStringStream is TEncoding-enabled).
Edit: Given the URL you provided, an example of calling verifyUser() would look like this:
const
ERPLYAccountCode = '...';
function verifyUser(const user, pass: string; const sessionLength: Integer = 3600): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TMemoryStream;
xDoc: IXMLDocument;
begin
Result := nil;
try
resStream := TMemoryStream.Create;
try
sPostData := TStringList.Create;
try
sPostData.Add('clientCode=' + ERPLYAccountCode);
sPostData.Add('request=verifyUser');
sPostData.Add('version=1.0');
sPostData.Add('responseType=XML');
sPostData.Add('responseMode=normal');
sPostData.Add('username=' + user);
sPostData.Add('password=' + pass);
sPostData.Add('sessionLength=' + IntToStr(sessionLength));
sHttpSocket := TIdHTTP.Create;
try
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(sHttpSocket);
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Post('https://www.erply.net/api/', sPostData, resStream);
finally
sHttpSocket.Free;
end;
finally
sPostData.Free;
end;
resStream.Position := 0;
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
finally
resStream.Free;
end;
except
on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end;
end;
end;