Konuyu Oyla:
  • Derecelendirme: 0/5 - 0 oy
  • 1
  • 2
  • 3
  • 4
  • 5
SAOP Servis Out of Memory Hatası
#1
Merhaba,

e-Fatura için hizmet aldığım firma SOAP servis üzerinden bilgileri sunuyor.

Kayıtlı GİB kullanıcı listesini önceden SOAP servisten sorunsuz çekiyordum. Bana biz zip dosya içeriği geliyordu. Bende zipi kayıt edip içeriğini açarak gelen xml dosyayı içeriğini parse ederek kullanıyordum.

Son zamanlarda mükellef firmaların artması ile bana sunulan liste çok fazla şişti ve artık çekmek istediğim zaman out of memory alıyorum.

Dönüş olarak dosya adı vs diğer bilgilerle birlikte asıl bilgi olan zip dosyanın içeriği TByteSOAPArray tipinde geliyor. Fakat boyutu çok fazla olduğu için işlem devam ederken hata alıyorum.

Bu konuda nasıl ilerlemeliyim? Böyle bir durumla karşılaşan oldu mu? Gelen bilgiyi bellek yerine dosyaya yazmak gibi bir mantık gördüm.
Fakat nasıl olacağına anlam veremedim. 

Kodum aşağıdaki gibi 27. satırda işlem devam ederken hata alıyorum.

    getServiceValues(adrWSDL, adrURL, adrSvc, adrPrt, usr, pass);
   web_servis := GetIPostBoxService(False, adrWSDL);

   m_login           := Login.Create;
   m_login_type2     := LoginType2.Create;
   m_getUserListNew  := getUserListNew.Create;

   m_login_type2.appStr   := ownerFatura.Database.FirmaBilgileri.eFtrAppStr;
   m_login_type2.userName := ownerFatura.Database.FirmaBilgileri.eFtrUserName;
   m_login_type2.passWord := ownerFatura.Database.FirmaBilgileri.eFtrPassWord;
   m_login_type2.version  := ownerFatura.Database.FirmaBilgileri.eFtrVersion;

   m_login.login := m_login_type2;
   m_login_response := web_servis.Login(m_login);

   //sessionID al
   m_sessionID := m_login_response.sessionID;

   m_getUserListNew.login := m_login.login;

   //tip bilgisini PKList(Posta Kutusu) / GBList(Gönderici Bilgisi) için ayarla
   if is_pk_list then
     m_getUserListNew.listType  := UserListType.PKLIST
   else
     m_getUserListNew.listType  := UserListType.GBLIST;

    m_userListNewResponse      := web_servis.getUserListNew(m_getUserListNew);
    //gelen base64 bilgiyi binary dosyaya çevir
    TGenel.Base64BinaryToFile(TArray<Byte>(m_userListNewResponse.getUserListNewResult.binaryData.Value),
                              m_userListNewResponse.getUserListNewResult.fileName,
                              ExtractFilePath(Application.ExeName));
PostgreSQL - Linux - Delphi, Poliüretan
WWW
Cevapla
#2
(10-03-2025, Saat: 15:01)3ddark Adlı Kullanıcıdan Alıntı: Merhaba,

e-Fatura için hizmet aldığım firma SOAP servis üzerinden bilgileri sunuyor.

Kayıtlı GİB kullanıcı listesini önceden SOAP servisten sorunsuz çekiyordum. Bana biz zip dosya içeriği geliyordu. Bende zipi kayıt edip içeriğini açarak gelen xml dosyayı içeriğini parse ederek kullanıyordum.

Son zamanlarda mükellef firmaların artması ile bana sunulan liste çok fazla şişti ve artık çekmek istediğim zaman out of memory alıyorum.

Dönüş olarak dosya adı vs diğer bilgilerle birlikte asıl bilgi olan zip dosyanın içeriği TByteSOAPArray tipinde geliyor. Fakat boyutu çok fazla olduğu için işlem devam ederken hata alıyorum.

Bu konuda nasıl ilerlemeliyim? Böyle bir durumla karşılaşan oldu mu? Gelen bilgiyi bellek yerine dosyaya yazmak gibi bir mantık gördüm.
Fakat nasıl olacağına anlam veremedim. 

Kodum aşağıdaki gibi 27. satırda işlem devam ederken hata alıyorum.

    getServiceValues(adrWSDL, adrURL, adrSvc, adrPrt, usr, pass);
   web_servis := GetIPostBoxService(False, adrWSDL);

   m_login           := Login.Create;
   m_login_type2     := LoginType2.Create;
   m_getUserListNew  := getUserListNew.Create;

   m_login_type2.appStr   := ownerFatura.Database.FirmaBilgileri.eFtrAppStr;
   m_login_type2.userName := ownerFatura.Database.FirmaBilgileri.eFtrUserName;
   m_login_type2.passWord := ownerFatura.Database.FirmaBilgileri.eFtrPassWord;
   m_login_type2.version  := ownerFatura.Database.FirmaBilgileri.eFtrVersion;

   m_login.login := m_login_type2;
   m_login_response := web_servis.Login(m_login);

   //sessionID al
   m_sessionID := m_login_response.sessionID;

   m_getUserListNew.login := m_login.login;

   //tip bilgisini PKList(Posta Kutusu) / GBList(Gönderici Bilgisi) için ayarla
   if is_pk_list then
     m_getUserListNew.listType  := UserListType.PKLIST
   else
     m_getUserListNew.listType  := UserListType.GBLIST;

    m_userListNewResponse      := web_servis.getUserListNew(m_getUserListNew);
    //gelen base64 bilgiyi binary dosyaya çevir
    TGenel.Base64BinaryToFile(TArray<Byte>(m_userListNewResponse.getUserListNewResult.binaryData.Value),
                              m_userListNewResponse.getUserListNewResult.fileName,
                              ExtractFilePath(Application.ExeName));

Hangi entegratörle çalışıyorsunuz ? Benimde e-fatura düzenlediğim bir programım var. Ben izibiz ile çalışıyorum. Web servis üzerinden doğrudan vergi numarası üzerinden mükellef sorgulayıp e-fatura mı, e-arşiv mükellefi mi anlayabiliyorum. Sizin entegratörünüzde de böyle bir fonksiyon olması lazım.
Cevapla
#3
(10-03-2025, Saat: 17:47)enigma Adlı Kullanıcıdan Alıntı:
(10-03-2025, Saat: 15:01)3ddark Adlı Kullanıcıdan Alıntı: Merhaba,

e-Fatura için hizmet aldığım firma SOAP servis üzerinden bilgileri sunuyor.

Kayıtlı GİB kullanıcı listesini önceden SOAP servisten sorunsuz çekiyordum. Bana biz zip dosya içeriği geliyordu. Bende zipi kayıt edip içeriğini açarak gelen xml dosyayı içeriğini parse ederek kullanıyordum.

Son zamanlarda mükellef firmaların artması ile bana sunulan liste çok fazla şişti ve artık çekmek istediğim zaman out of memory alıyorum.

Dönüş olarak dosya adı vs diğer bilgilerle birlikte asıl bilgi olan zip dosyanın içeriği TByteSOAPArray tipinde geliyor. Fakat boyutu çok fazla olduğu için işlem devam ederken hata alıyorum.

Bu konuda nasıl ilerlemeliyim? Böyle bir durumla karşılaşan oldu mu? Gelen bilgiyi bellek yerine dosyaya yazmak gibi bir mantık gördüm.
Fakat nasıl olacağına anlam veremedim. 

Kodum aşağıdaki gibi 27. satırda işlem devam ederken hata alıyorum.

    getServiceValues(adrWSDL, adrURL, adrSvc, adrPrt, usr, pass);
   web_servis := GetIPostBoxService(False, adrWSDL);

   m_login           := Login.Create;
   m_login_type2     := LoginType2.Create;
   m_getUserListNew  := getUserListNew.Create;

   m_login_type2.appStr   := ownerFatura.Database.FirmaBilgileri.eFtrAppStr;
   m_login_type2.userName := ownerFatura.Database.FirmaBilgileri.eFtrUserName;
   m_login_type2.passWord := ownerFatura.Database.FirmaBilgileri.eFtrPassWord;
   m_login_type2.version  := ownerFatura.Database.FirmaBilgileri.eFtrVersion;

   m_login.login := m_login_type2;
   m_login_response := web_servis.Login(m_login);

   //sessionID al
   m_sessionID := m_login_response.sessionID;

   m_getUserListNew.login := m_login.login;

   //tip bilgisini PKList(Posta Kutusu) / GBList(Gönderici Bilgisi) için ayarla
   if is_pk_list then
     m_getUserListNew.listType  := UserListType.PKLIST
   else
     m_getUserListNew.listType  := UserListType.GBLIST;

    m_userListNewResponse      := web_servis.getUserListNew(m_getUserListNew);
    //gelen base64 bilgiyi binary dosyaya çevir
    TGenel.Base64BinaryToFile(TArray<Byte>(m_userListNewResponse.getUserListNewResult.binaryData.Value),
                              m_userListNewResponse.getUserListNewResult.fileName,
                              ExtractFilePath(Application.ExeName));

Hangi entegratörle çalışıyorsunuz ? Benimde e-fatura düzenlediğim bir programım var. Ben izibiz ile çalışıyorum. Web servis üzerinden doğrudan vergi numarası üzerinden mükellef sorgulayıp e-fatura mı, e-arşiv mükellefi mi anlayabiliyorum. Sizin entegratörünüzde de böyle bir fonksiyon olması lazım.

Merhabalar,

Evet katılıyorum size.

Entegratör firmanızda VKN/TCKN ile direkt mükellef bazlı sorgulama mutlaka vardır.
Bu fonksyion ile sorgulamak daha hızlı sonuç verir.

Diğer türlü her defasında .zip dosyasını indirmek bir külfet, bir de gelen XML parse etmek ayrı bir külfet.

Cari açılışlarında VKN/TCKN ile tek firma sorgulatmak sizin için daha iyi olur.

Ama farklı bir amaç ile indirip işlem yapıyorsanız bir şey diyemem.

Kolay gelsin.
Amaç, bilginin de/aklın da zekat'ını vermek.
Cevapla
#4
Logo kullanıyorum. Yıllar önce uygulamayı yaptığımda tckn/vkn bazlı sorgulaması yoktu ve yazılım bu duruma göre kurguladım.

Güncel servis kodlarını tekrar incelerim.
Fakat benim anlamaya çalıştığım böyle bir senaryoda nasıl bir yol izlenebilir.

Farz edelim ki dışarıdan bir API hizmeti alıyorsunuz ve böyle bir büyük dosyanın alınması gerekiyor.
Soru nasıl yaparım?
PostgreSQL - Linux - Delphi, Poliüretan
WWW
Cevapla
#5
Gelen dosyanın stream olduğunu değerlendirerek tfilestream'in çözüm olacağını öngörebiliriz.

Buradaki soru file stream olarak 50 MB indirdik diyelim. Bunu bir de xml deserialization/parse meselesi var.

Sadece  xml tutarlılık/validation kontrolü için dahi hafıza lazım. Aynı sorun mevcut yapıda yine devam edecektir diye değerlendiriyorum.

İlk düşünülecek konu 64 bitlik değilse en azından uygulamanızı 64 bit dönüşüm yapılması gerekecektir. Zaten bunu yapmışsınızdır. Geriye böl parçala kontrol yöntemi ile string operasyonları ile xml bloklarını alıp sayfalamayı yerelde yapmak yoluna gidebilirsiniz.
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif
Cevapla
#6
Cevaplar için teşekkürler.
@mrmarman üstadım uygulamayı postu yazmadan önce 64 bit çevirmiştim. Derleme anında hata yok. Fakat bir bileşen dll ile ilgili uygulamayı çalıştırma anında dll hatası alıyordum. Bu nedenle henüz 64 bit deneyemedim.

Fakat ilk iş olarak bunu deneyeceğim.
Böyle bir SOAP servis için büyük dosya geldiğinde memory hatası almadan nasıl dosyayı alırım sorununu yapay zekaya sorduğumda, aşağıdaki gibi bir örnek verdi.

procedure DownloadFile;
var
 HTTPRIO: THTTPRIO;
 FileStream: TFileStream;
 URL: string;
begin
 HTTPRIO := THTTPRIO.Create(nil);
 try
   URL := 'http://your-soap-service-url/soapendpoint';
   HTTPRIO.URL := URL;
   HTTPRIO.Service := 'YourServiceName';
   HTTPRIO.Port := 'YourPortName';

   // Dosyayı doğrudan disk üzerine yazacak TFileStream oluştur
   FileStream := TFileStream.Create('C:\Temp\BigFile.zip', fmCreate);
   try
     // SOAP servisten stream olarak indir ve doğrudan diske yaz
     HTTPRIO.HTTPWebNode.Get('http://your-soap-service-url/DownloadBigFile', FileStream);
   finally
     FileStream.Free;
   end;
 finally
   HTTPRIO.Free;
 end;

 ShowMessage('Dosya başarıyla indirildi!');
end;
PostgreSQL - Linux - Delphi, Poliüretan
WWW
Cevapla
#7
Bu sonucu direkt download değil de onExecute benzeri bir yerde downloadı yapmak ve yaparken de her dosyanın adına saat tarih ekli veya sayaçlı (son dosyadan bir fazla) bir isim vererek yaparsanız sağlıklı olur. Thread tarzı işlemler olduğu için bu gerekli olabilir. Tabi kendi threadinizi açarsanız o başka
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif
Cevapla
#8
(11-03-2025, Saat: 08:46)3ddark Adlı Kullanıcıdan Alıntı: Logo kullanıyorum. Yıllar önce uygulamayı yaptığımda tckn/vkn bazlı sorgulaması yoktu ve yazılım bu duruma göre kurguladım.

Güncel servis kodlarını tekrar incelerim.
Fakat benim anlamaya çalıştığım böyle bir senaryoda nasıl bir yol izlenebilir.

Farz edelim ki dışarıdan bir API hizmeti alıyorsunuz ve böyle bir büyük dosyanın alınması gerekiyor.
Soru nasıl yaparım?

Bildiğim kadarıyla bahse konu olan .zip formatındaki mükellef listesi genellikle günde bir kere indirilip üzerinde çalışıyorsunuz. Bu liste GİB tarafından her 2 saatte bir güncelleniyor diye biliyorum. Bundan 4, 5 sene önce boyut olarak daha küçüktü ancak e-fatura mükelleflerinin sayısı arttıkça bu liste de şişti. 
İzibiz geçen sene doğrudan mükellef sorgulama servisini kaldıracağını ve bu .zip dosya üzerinden işlem yapılmasını belirtti. Ancak daha sonra bu uygulamadan vazgeçti. (sanırım bu dosya boyutu yüzden)
Cevapla
#9
Eğer konu sadece mükellef sorgulama ise aşağıdaki kod ile bu sorgulamayı yapabilirsiniz. (SOAP tarafında sizin bu imkanınız bulunmuyorsa

EXE dosyasını denemek isterseniz bu linkten indirebilirsiniz.
Aynı zamanda WEB servis olarak da deneyebilirsiniz. PORT numarası 7000 olarak çalışıyor.
Yani EXE çalışıyor iken herhangi bir web browser ile açıp aşağıdaki şekilde yazdığınızda cevap alabilirsiniz. Bu dediğim gibi sadece yapılabilecekler hakkında örnektir.

http://localhost:7000?vd=5200043963


Böylece hem dosya indirme yükünü ve zaman kaybını üzerinizden atarsınız, hem de daima en güncel haline hızlı bir şekilde erişme imkanı elde etmiş olursunuz.

Hatta şunu da yapabilirsiniz. 
Kendi WEB Servisinizi kurup http://ip:port?vd=1234567890  gibi bir GET method çağrısı ile veya post çağrısı ile tüm kullanıcılarınızın çağrılarını tek noktadan yönetebilir, olası captcha değişimi vb. durumlarda sadece servis kodundaki güncelleme ile tüm istemcilerin sorununu çözebilirsiniz. 

Ticari bir uygulama olduğundan OCR işini bir kaç farklı şekilde kendiniz çözebileceğinizi değerlendiriyorum. O kısmı paylaşmayacağım. Ben kendim medula için hazırladığım OCR ile gösterim yapacağım.

Basitten başlayarak
1. captcha resmi ekrana getirip kullanıcı tarafından girilmesini sağlamak,
2. tesseract OCR gibi hazır açık kaynak paket kullanmak
3. kendi grafik bilginizi konuşturup (GiB sayfasındaki Captcha görece basit olduğundan) kendi OCR fonksiyonunuzu oluşturmak
4. belki ücretli varsa üçüncü parti OCR paketi kullanmak




Uses  System.Net.HttpClientComponent,
      System.Net.HttpClient,
      JPEG,
      System.UITypes;

type
  tMukellefInfo = record
    Guncelleme  : TDate;
    Toplam      : Integer;
    Status      : boolean;
    Msg         : string;
  end;

function MukellefSearch( const aVD: String ): tMukellefInfo;
const
  LKayitli    = 'Mükellef kayıtlıdır.';
  LBulunamadi = 'Arama işlemi sonucunda kayıt bulunamadı!';
  LCaptchaErr = 'Güvenlik kodu hatalı, lütfen kontrol ediniz!';
  LSrcToplam  = '<p>Toplam <u>';
  LSrcGuncel  = 'ncelleme Tarihi:';
var
  LClient     : System.Net.HttpClientComponent.TNetHTTPClient;
  LResponse   : System.Net.HttpClient.IHTTPResponse;
  LParam      : TStringList;
  LJPG        : TJPEGImage;
  LBitmap     : TBitmap;
  LCaptcha    : String;
  LBlok       : string;
begin
  result  := default(tMukellefInfo);

  LClient := TNetHTTPClient.Create(nil);
  try
    LClient.UserAgent  := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36';

    LResponse := LClient.Get( 'https://sorgu.efatura.gov.tr/kullanicilar/xliste.php' );

    LBlok := LResponse.ContentAsString;
    if Pos(LSrcToplam, LBlok) > 0 then
    begin
      System.Delete(LBlok, 1, Pos(LSrcToplam, LBlok) + length(LSrcToplam)-1 );
      Result.Toplam := StrToInt(Copy(LBlok, 1, Pos('<', LBlok)-1));
    end;
    LBlok := LResponse.ContentAsString;
    if Pos(LSrcGuncel, LBlok) > 0 then
    begin
      System.Delete(LBlok, 1, Pos(LSrcGuncel, LBlok) + length(LSrcGuncel)-1 );
      Result.Guncelleme := StrToDate(Copy(LBlok, 1, Pos('<', LBlok)-1));
    end;

  //TStringStream(LResponse.ContentStream).SaveToFile( extractFilePath(ParamStr(0)) + 'Gelen.html' );

    LResponse := LClient.Get( 'https://sorgu.efatura.gov.tr/kullanicilar/img.php' );

    LJPG  := TJPEGImage.Create;
    try
      LJPG.LoadFromStream( LResponse.ContentStream );

      LBitmap := TBitmap.Create;
      try
        LBitmap.SetSize( LJPG.Width, LJPG.Height );
        LBitmap.Canvas.Draw(1,1, LJPG);

        // OCR İşlemi...
        LCaptcha := OCR_Image( LBitmap );

      finally
        FreeAndNil(LBitmap);
      end;

    finally
      FreeAndNil(LJPG);
    end;

    LParam := TStringList.Create;
    try
      LParam.Values['captcha_code']  :=  LCaptcha;
      LParam.Values['search_string'] :=  aVD;
      LParam.Values['submit']        := 'Ara';

      LResponse := LClient.Post( 'https://sorgu.efatura.gov.tr/kullanicilar/xliste.php', LParam );

      if LResponse.StatusCode = 200
        then
          begin
            if pos( LCaptchaErr, LResponse.ContentAsString ) > 0
            then
              result.Msg := '[-] ' + LCaptchaErr
            else
              if pos( LBulunamadi, LResponse.ContentAsString ) > 0
              then
                result.Msg := '[-] ' + LBulunamadi
              else
                if pos( LKayitli, LResponse.ContentAsString ) > 0
                then begin
                  result.Msg    := '[+] Mükellef mevcut';
                  result.Status := true;
                end
                else
                  result.Msg := '[?] ' + sLineBreak + LResponse.ContentAsString;

          end
        else
          result.Msg := '[ERROR]' + IntToStr(LResponse.StatusCode)
            + sLineBreak + LResponse.ContentAsString;

    finally
      FreeAndNil(LParam);
    end;

  finally
    FreeAndNil( LClient );
  end;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  LResponse : tMukellefInfo;
begin

  LResponse := MukellefSearch( Edit1.Text );

  With LResponse do
    MessageDlg( Format( 'Güncel   : %s' + slineBreak
                      + 'Toplam  : %d' + sLineBreak  + sLineBreak
                      + 'Mükellef : %s',
                      [ FormatDateTime( 'DD.MM.YYYY', Guncelleme )
                       ,Toplam
                       ,Msg
                      ]), TMsgDlgType.mtInformation, [mbOk], 0 );

end;




h3yadt9zetk9vffrmyzv.gif


jzztxp7sd388vsuhh0gm.gif
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif
Cevapla
#10
Yeni bir 64bit proje oluşturup servisi çağırdığımda sorunsuz olarak alabildim.
PK List için Zip dosya boyutu yaklaşık 100mb. Dosya açılınca XML boyutu 1,125 GB oluyor.
GB List için Zip dosya boyutu yaklaşık 96mb. Dosya açılınca XML boyutu 1,089 GB oluyor.
Fakat anlamadığım nokta şu 100Mb gibi küçük bir dosya için memory hatası vermesi çok anlamsız geliyor.
İşlem sırasında görev yöneticisinden takip ettiğim zaman çok fazla bellek kullanımı da yok. en fazla 130MB civarında gördüm.
Tabi bu işlemlerin tamamında 64bit için konuşuyorum.
PostgreSQL - Linux - Delphi, Poliüretan
WWW
Cevapla


Konu ile Alakalı Benzer Konular
Konular Yazar Yorumlar Okunma Son Yorum
  Gelen tablo veri akışı (TDS) Hatası apachi2006 1 1.048 24-01-2024, Saat: 16:20
Son Yorum: apachi2006
  UniGui Memory Leak ikurt07 5 2.602 08-05-2023, Saat: 09:02
Son Yorum: yhackup
Photo Türkçe Karekter sorunu ( Web Servis) powerghost 11 8.459 30-04-2020, Saat: 14:11
Son Yorum: mrmarman
  Web Servis Client - Çok büyük response myasa 3 3.540 28-05-2019, Saat: 12:29
Son Yorum: r3n4m3
  web servis ile veritabanı bağlantısı nasıl yapılır? alisahin 3 4.296 28-05-2019, Saat: 08:52
Son Yorum: r3n4m3



Konuyu Okuyanlar: 1 Ziyaretçi