# Resuming downloads

Resuming downloads is a useful feature that saves bandwidth and time by allowing the retrieval process to continue from where it left off, in case of interruptions or disconnections.&#x20;

It calculates where to restart by determining the `startBlock` when processing download requests. This computation occurs in the `calculateShardParams` method, which interacts with the `fileHandler` to establish the file size. The derived size is then employed to call the `Seek()` method of the `DownloadRequest`. This method adjusts the `offset` field of the `DownloadRequest`, which is subsequently used to calculate the `startBlock`.

For local files, the system opens them in append mode if the file already exists and has a size that's greater than 0.

This resumption feature is illustrated in the code sections below. These functions together facilitate the operation of the download resumption process:

* `calculateShardsParams()`: Executes the actual resumption logic by calculating shard parameters. If there's an offset (indicating an existing incomplete file), it adjusts the `startBlock` accordingly.

```
func (req *DownloadRequest) calculateShardsParams(
	fRef *fileref.FileRef, remotePathCB string) (chunksPerShard int64, err error) {

	size := fRef.ActualFileSize
	if req.contentMode == DOWNLOAD_CONTENT_THUMB {
		if fRef.ActualThumbnailSize == 0 {
			return 0, errors.New("invalid_request", "Thumbnail does not exist")
		}
		size = fRef.ActualThumbnailSize
	}
	req.size = size
	req.encryptedKey = fRef.EncryptedKey
	req.chunkSize = int(fRef.ChunkSize)

	effectivePerShardSize := (size + int64(req.datashards) - 1) / int64(req.datashards)
	effectiveBlockSize := fRef.ChunkSize
	if fRef.EncryptedKey != "" {
		effectiveBlockSize -= EncryptionHeaderSize + EncryptedDataPaddingSize
	}

	req.effectiveBlockSize = int(effectiveBlockSize)

	chunksPerShard = (effectivePerShardSize + effectiveBlockSize - 1) / effectiveBlockSize

	info, err := req.fileHandler.Stat()
	if err != nil {
		return 0, err
	}
	_, err = req.Seek(info.Size(), io.SeekStart)
	if err != nil {
		return 0, err
	}

	effectiveChunkSize := effectiveBlockSize * int64(req.datashards)
	if req.offset > 0 {
		req.startBlock += req.offset / effectiveChunkSize
	}

	if req.endBlock == 0 || req.endBlock > chunksPerShard {
		req.endBlock = chunksPerShard
	}

	if req.startBlock >= req.endBlock {
		err = errors.New("invalid_block_num", "start block should be less than end block")
		return 0, err
	}

	return
}
```

* `prepareAndOpenLocalFile()`: Handles the local file, checking if it exists, and opening in append mode or creating it as needed.

```
func (a *Allocation) prepareAndOpenLocalFile(localPath string, remotePath string) (*os.File, string, bool, error) {
	var toKeep bool

	if !a.isInitialized() {
		return nil, "", toKeep, notInitialized
	}

	var localFilePath string

	// If the localPath has a file extension, treat it as a file. Otherwise, treat it as a directory.
	if filepath.Ext(localPath) != "" {
		localFilePath = localPath
	} else {
		localFileName := filepath.Base(remotePath)
		localFilePath = filepath.Join(localPath, localFileName)
	}

	// Create necessary directories if they do not exist
	dir := filepath.Dir(localFilePath)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err := os.MkdirAll(dir, 0744); err != nil {
			return nil, "", toKeep, err
		}
	}

	var f *os.File
	info, err := os.Stat(localFilePath)
	if errors.Is(err, os.ErrNotExist) {
		f, err = os.OpenFile(localFilePath, os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			return nil, "", toKeep, errors.Wrap(err, "Can't create local file")
		}
	} else {
		f, err = os.OpenFile(localFilePath, os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			return nil, "", toKeep, errors.Wrap(err, "Can't open local file in append mode")
		}
		if info.Size() > 0 {
			toKeep = true
		}
	}

	return f, localFilePath, toKeep, nil
}
```
